From: notaz Date: Tue, 23 Jun 2009 09:14:46 +0000 (+0300) Subject: 2.4 kernel support, manual module loading for 2.6 X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?p=warm.git;a=commitdiff_plain;h=cc9517321fda63efa03335ff0c47540394fd93ce 2.4 kernel support, manual module loading for 2.6 --- diff --git a/Makefile b/Makefile index 4cf37a6..3a8dd2d 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ CFLAGS += -Wall -O2 WARM_A = libwarm.a WARM_SO = libwarm.so -OBJS = warm.o sys_cacheflush.o +OBJS = warm.o all: $(WARM_A) $(WARM_SO) test @@ -14,8 +14,8 @@ $(WARM_A): $(OBJS) $(WARM_SO): $(OBJS) $(CC) $(CFLAGS) $^ -o $@ -shared -test: test.o - $(CC) $(CFLAGS) $^ -o $@ libwarm.a +test: test.o $(WARM_A) + $(CC) $(CFLAGS) $^ -o $@ clean: $(RM) $(WARM_A) $(WARM_SO) $(OBJS) test test.o diff --git a/module/Makefile_2.4 b/module/Makefile_2.4 index 14e286a..be98e74 100644 --- a/module/Makefile_2.4 +++ b/module/Makefile_2.4 @@ -4,7 +4,8 @@ endif CROSS_COMPILE = arm-linux- INCLUDE = $(KERNEL_DIR)/include -CPPFLAGS = -O2 -DMODULE -D__KERNEL__ -I${INCLUDE} +CPPFLAGS = -Wall -O2 -DMODULE -D__KERNEL__ -msoft-float -nostdinc -isystem ${INCLUDE} +CPPFLAGS+= -isystem $(shell $(CC) -print-file-name=include) CC = $(CROSS_COMPILE)gcc LD = $(CROSS_COMPILE)ld diff --git a/module/warm_main.c b/module/warm_main.c index 21c54d4..bbbcd96 100644 --- a/module/warm_main.c +++ b/module/warm_main.c @@ -149,14 +149,16 @@ static int do_set_cb_virt(int in_cb, int is_set, u32 addr, u32 size) u32 desc1, desc2; u32 *pgtable, *cpt; u32 start, end; + u32 mask; 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); + mask = PAGE_SIZE - 1; + size += addr & mask; + size = (size + mask) & ~mask; addr &= ~(PAGE_SIZE - 1); start = addr; @@ -221,6 +223,7 @@ static int do_virt2phys(unsigned long *_addr) case 3: /* fine table */ cpt = __va(desc1 & 0xfffff000); desc2 = cpt[(addr >> 10) & 0x3ff]; + break; default: return -EINVAL; } @@ -228,13 +231,13 @@ static int do_virt2phys(unsigned long *_addr) switch (desc2 & 3) { case 1: /* large page */ - *_addr = (desc2 & 0xffff0000) | (addr & 0xffff); + *_addr = (desc2 & ~0xffff) | (addr & 0xffff); break; case 2: /* small page */ - *_addr = (desc2 & 0xfffff000) | (addr & 0x0fff); + *_addr = (desc2 & ~0x0fff) | (addr & 0x0fff); break; case 3: /* tiny page */ - *_addr = (desc2 & 0xfffffc00) | (addr & 0x03ff); + *_addr = (desc2 & ~0x03ff) | (addr & 0x03ff); break; default: return -EINVAL; diff --git a/module/warm_ops.S b/module/warm_ops.S index 541d69d..499f760 100644 --- a/module/warm_ops.S +++ b/module/warm_ops.S @@ -1,11 +1,20 @@ -/* vim:filetype=armasm -*/ - -#include +/* + * Cache operations + * + * 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. + */ + +#ifndef AUTOCONF_INCLUDED +#include +#endif #define CACHELINE_SZC #32 -#ifndef CONFIG_CPU_ARM926T +#if !defined(CONFIG_CPU_ARM926T) && !defined(CONFIG_CPU_ARM920T) #error CPU not supported #endif @@ -13,18 +22,41 @@ .align 2 .global warm_cop_clean_inval_d +.global warm_cop_clean_d + +#ifdef CONFIG_CPU_ARM926T + 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 +#else + +/* comes from Linux kernel code */ +.macro warm_cop_wholecache_dop crm + mov r1, #0x000000e0 @ 8 segments +1: orr r3, r1, #0xfc000000 @ 64 entries +2: mcr p15, 0, r3, c7, \crm, 2 @ D index op + subs r3, r3, #1<<26 + bcs 2b @ entries 63 to 0 + subs r1, r1, #1<<5 + bcs 1b @ segments 7 to 0 + bx lr +.endm + +warm_cop_clean_inval_d: + warm_cop_wholecache_dop c14 + +warm_cop_clean_d: + warm_cop_wholecache_dop c10 + +#endif .global warm_cop_inval_d warm_cop_inval_d: @@ -86,3 +118,6 @@ warm_drain_wb_inval_tlb: mcr p15, 0, r0, c8, c7, 0 bx lr + +@ vim:filetype=armasm + diff --git a/sys_cacheflush.S b/sys_cacheflush.S deleted file mode 100644 index 61dac35..0000000 --- a/sys_cacheflush.S +++ /dev/null @@ -1,22 +0,0 @@ -@ 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 deleted file mode 100644 index 27f3c4c..0000000 --- a/sys_cacheflush.h +++ /dev/null @@ -1,3 +0,0 @@ - -void sys_cacheflush(void *start_addr, void *end_addr); - diff --git a/test.c b/test.c index 8b315f9..55dfe7b 100644 --- a/test.c +++ b/test.c @@ -149,15 +149,15 @@ void coherency_test(void) { volatile unsigned char *buff_mapped; volatile unsigned char *buff_vol; - unsigned long buff_phys, align; - unsigned char buff_mapped_vals[5]; + unsigned long buff_phys, mapped_phys, align; + unsigned char buff_mapped_vals[6]; unsigned char buff_vals[5]; int random_offs; int memdev; srand(time(NULL)); - buff_phys = warm_virt2phys(buff); + buff_phys = warm_virt2phys(buff_mid); align = buff_phys & 0xfff; memdev = open("/dev/mem", O_RDONLY | O_SYNC); @@ -166,7 +166,9 @@ void coherency_test(void) return; } - /* the mapping is valid for 1 page only. */ + /* the mapping is valid for 1 page only. + * Also it doesn't work for GP2X F100 (2.4 kernels?), + * only upper mem maps correctly there. */ buff_mapped = mmap(NULL, 0x1000, PROT_READ, MAP_SHARED, memdev, buff_phys & ~0xfff); if (buff_mapped == MAP_FAILED) { @@ -175,9 +177,14 @@ void coherency_test(void) } buff_mapped += align; + buff_mapped_vals[5] = buff_mapped[0]; /* touch */ + mapped_phys = warm_virt2phys((void *)buff_mapped); + if (buff_phys != mapped_phys) + printf("warning: mmap requested %08lx, got %08lx\n", buff_phys, mapped_phys); + random_offs = rand() % (0x1000 - align); - buff_vol = (volatile void *)buff; + buff_vol = (volatile void *)buff_mid; buff_vals[0] = buff_vol[random_offs]; buff_mapped_vals[0] = buff_mapped[random_offs]; /* incremented: */ diff --git a/warm.c b/warm.c index f56a372..fd3fb4c 100644 --- a/warm.c +++ b/warm.c @@ -36,18 +36,77 @@ #include #include #include +#include #include #define WARM_CODE #include "warm.h" -#include "sys_cacheflush.h" + +extern long init_module(void *, unsigned long, const char *); /* provided by glibc */ static int warm_fd = -1; +static int kernel_version; + +static void sys_cacheflush(void *start, void *end) +{ +#ifdef __ARM_EABI__ + /* EABI version */ + int num = __ARM_NR_cacheflush; + __asm__("mov r0, %0 ;" + "mov r1, %1 ;" + "mov r2, #0 ;" + "mov r7, %2 ;" + "swi 0" : : "r" (start), "r" (end), "r" (num) + : "r0", "r1", "r2", "r3", "r7"); +#else + /* OABI */ + __asm__("mov r0, %0 ;" + "mov r1, %1 ;" + "mov r2, #0 ;" + "swi %2" : : "r" (start), "r" (end), "i" __ARM_NR_cacheflush + : "r0", "r1", "r2", "r3"); +#endif +} + +static int manual_insmod_26(const char *fname, const char *opts) +{ + unsigned long len, read_len; + int ret = -1; + void *buff; + FILE *f; + + f = fopen(fname, "rb"); + if (f == NULL) + return -1; + + fseek(f, 0, SEEK_END); + len = ftell(f); + fseek(f, 0, SEEK_SET); + + buff = malloc(len); + if (buff == NULL) + goto fail0; + + read_len = fread(buff, 1, len, f); + if (read_len != len) { + fprintf(stderr, "failed to read module\n"); + goto fail1; + } + + ret = init_module(buff, len, opts); + +fail1: + free(buff); +fail0: + fclose(f); + return ret; +} int warm_init(void) { struct utsname unm; - char buff[128]; + char buff1[32], buff2[128]; + int ret; warm_fd = open("/proc/warm", O_RDWR); if (warm_fd >= 0) @@ -55,23 +114,56 @@ int warm_init(void) memset(&unm, 0, sizeof(unm)); uname(&unm); - snprintf(buff, sizeof(buff), "/sbin/insmod warm_%s.ko verbose=1", unm.release); + + if (strlen(unm.release) < 3 || unm.release[1] != '.') { + fprintf(stderr, "unexpected version string: %s\n", unm.release); + goto fail; + } + kernel_version = ((unm.release[0] - '0') << 4) | (unm.release[2] - '0'); + + snprintf(buff1, sizeof(buff1), "warm_%s.%s", unm.release, kernel_version >= 0x26 ? "ko" : "o"); + snprintf(buff2, sizeof(buff2), "/sbin/insmod %s verbose=1", buff1); /* try to insmod */ - system(buff); + ret = system(buff2); + if (ret != 0) { + fprintf(stderr, "system/insmod failed: %d %d\n", ret, errno); + if (kernel_version >= 0x26) { + ret = manual_insmod_26(buff1, "verbose=1"); + if (ret != 0) + fprintf(stderr, "manual insmod also failed: %d\n", ret); + } + } + warm_fd = open("/proc/warm", O_RDWR); if (warm_fd >= 0) return 0; +fail: 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"); + char cmd[64]; + + if (warm_fd < 0) + return; + + close(warm_fd); + warm_fd = -1; + + if (kernel_version < 0x26) { + struct utsname unm; + memset(&unm, 0, sizeof(unm)); + uname(&unm); + snprintf(cmd, sizeof(cmd), "/sbin/rmmod warm_%s", unm.release); + } + else + strcpy(cmd, "/sbin/rmmod warm"); + + system(cmd); } int warm_cache_op_range(int op, void *addr, unsigned long size) @@ -80,6 +172,7 @@ int warm_cache_op_range(int op, void *addr, unsigned long size) int ret; if (warm_fd < 0) { + /* note that this won't work for warm_cache_op_all */ sys_cacheflush(addr, (char *)addr + size); return -1; } @@ -99,7 +192,7 @@ int warm_cache_op_range(int op, void *addr, unsigned long size) int warm_cache_op_all(int op) { - return warm_cache_op_range(op, NULL, (size_t)-1); + return warm_cache_op_range(op, NULL, (unsigned long)-1); } int warm_change_cb_range(int cb, int is_set, void *addr, unsigned long size)