/*--------------------------------------------------------------------------------*\ * fuer Module statt virtuellen Speicher Kernelspeicher verwenden * (Module laufen teilweise im FASTIRQ!) \*--------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static unsigned long module_alloc_size_list_base; static unsigned int module_alloc_size_list_size; static DEFINE_MUTEX(kernel_module_mutex); /*--- #define DBG_TRC(args...) printk(KERN_ERR args) ---*/ #define DBG_TRC(args...) /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int __init reserve_bootmem(unsigned long addr, unsigned long size, int flags) { #error "Reserve Bootmem is not yet implemented." return 0; } static struct _module_alloc_size_list { atomic_t alloc; unsigned long size; unsigned long addr; char name[64]; enum _module_alloc_type_ type; } module_alloc_size_list[50]; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static char *module_load_white_list[] = { "pcmlink", "avm_dect", "capi_codec", "isdn_fbox_fon5", "rtc_avm", "krtp", "ulpcmlink", "nlaudio", NULL }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int module_is_whitelisted(char *name) { char **p; p = module_load_white_list; while(*p) { if(!strcmp(*p, name)) { return 1; } p++; } return 0; } static struct resource module_param; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ int is_kernel_module_addr(unsigned long addr) { unsigned int phys_addr = __virt_to_phys(addr); return (phys_addr >= module_param.start) && (phys_addr < module_param.end); } /*--------------------------------------------------------------------------------*\ * Initiale Vorbereitungen fuer kernel-bootmem * es wird ein extra module_memory reserviert, so dass Treiber direkt im Kernelspeicher * geladen werden koennen. * Dies wird z.B. unbedingt benoetigt, falls Module die FASTIRQ-Schnittstelle verwenden * will (es duerfen im FASTIRQ-Kontext keine TLB-Exception auftreten!) \*--------------------------------------------------------------------------------*/ void __init module_alloc_bootmem_init(struct resource *res, unsigned long start_addr) { unsigned long aligned_start_addr; unsigned int i; if(module_alloc_size_list_base) { return; } if (avm_kernel_module_memory_config) { struct _avm_kernel_module_memory_config *CMod = avm_kernel_module_memory_config; module_alloc_size_list_size = 0; while (CMod->name) { if(module_is_whitelisted(CMod->name)) { module_alloc_size_list_size += ((CMod->size + PAGE_SIZE) & ~(PAGE_SIZE - 1)) + PAGE_SIZE; /*--- inkl. Reserve-Page fuer Entwicklung -> groesseren Treiber nachladen ---*/ } CMod++; } } else { module_alloc_size_list_size = 4 << 20; printk(KERN_ERR "[module-alloc-by-name] warning fix module_alloc_size_list_size used - todo\n"); } if (module_alloc_size_list_size == 0UL) { printk(KERN_ERR "[module-alloc-by-name] 'modulemem' not set, " "function disabled\n"); return; } aligned_start_addr = (start_addr + PAGE_SIZE - 1) & PAGE_MASK; module_param.start = __virt_to_phys(aligned_start_addr); module_param.end = module_param.start + module_alloc_size_list_size - 1; module_param.name = "module memory"; module_param.flags = IORESOURCE_MEM | IORESOURCE_BUSY; printk(KERN_ERR "[module-alloc] (start 0x%x end 0x%x) virt=0x%lx\n", module_param.start, module_param.end, aligned_start_addr); #if 0 if (request_resource(res, &module_param)) { printk(KERN_ERR "[module-alloc] failed 0x%x bytes at 0x%lx\n", module_alloc_size_list_size, aligned_start_addr); return; } #endif if(reserve_bootmem(module_param.start, module_param.end - module_param.start + 1, BOOTMEM_DEFAULT)) { printk(KERN_ERR "[module-alloc] reserve memory for module-load failed (start 0x%x end 0x%x)\n", module_param.start, module_param.end); return; } module_alloc_size_list_base = aligned_start_addr; printk(KERN_ERR "[module-alloc] use 0x%x bytes at 0x%lx\n", module_alloc_size_list_size, module_alloc_size_list_base); for(i = 0 ; i < ARRAY_SIZE(module_alloc_size_list); i++) { atomic_set(&module_alloc_size_list[i].alloc, 0); } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ char *module_alloc_find_module_name(char *buff, char *end, unsigned long addr) { unsigned int i; unsigned int len; mutex_lock(&kernel_module_mutex); for(i = 0 ; i < ARRAY_SIZE(module_alloc_size_list); i++) { if(atomic_read(&module_alloc_size_list[i].alloc) == 0) continue; if(addr < module_alloc_size_list[i].addr) continue; if(addr >= module_alloc_size_list[i].size + module_alloc_size_list[i].addr) continue; len = snprintf(buff, end - buff, "0x%08lx (%s + 0x%lx) [%s]", addr, module_alloc_size_list[i].name, addr - module_alloc_size_list[i].addr, module_alloc_size_list[i].name); mutex_unlock(&kernel_module_mutex); return buff + len; } mutex_unlock(&kernel_module_mutex); len = snprintf(buff, end - buff, "0x%08lx", addr); return buff + len; } /*------------------------------------------------------------------------------------------*\ * ret: 0 addr-range freed for re-use \*------------------------------------------------------------------------------------------*/ int module_alloc_size_list_free(unsigned long addr) { unsigned int i; mutex_lock(&kernel_module_mutex); for(i = 0 ; i < ARRAY_SIZE(module_alloc_size_list); i++) { if(module_alloc_size_list[i].addr == addr) { if(atomic_read(&module_alloc_size_list[i].alloc) == 1) { DBG_TRC("[%s] module=%s pointer 0x%lx found in kernel-module-list -freed for re-use\n", __func__, module_alloc_size_list[i].name, addr); memset((void *)module_alloc_size_list[i].addr, 0xCC, module_alloc_size_list[i].size); /*--- destroy contents ---*/ atomic_set(&module_alloc_size_list[i].alloc, 0); mutex_unlock(&kernel_module_mutex); return 0; } } } DBG_TRC("[%s] pointer 0x%lx not found in kernel-module-list\n", __func__, addr); mutex_unlock(&kernel_module_mutex); return -EFAULT; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_CHECK_TIMER_ON_FREED_MODULE) int module_alloc_check_pointer(unsigned long addr, char **name) { unsigned int i; *name = NULL; for(i = 0 ; i < ARRAY_SIZE(module_alloc_size_list); i++) { if(module_alloc_size_list[i].addr > addr) { continue; } if(module_alloc_size_list[i].addr + module_alloc_size_list[i].size <= addr) { continue; } if(atomic_read(&module_alloc_size_list[i].alloc) == 1) { return 0; } *name = module_alloc_size_list[i].name; return -1; } /*--- printk(KERN_ERR "[module-alloc-by-name] pointer 0x%lx isn't in kernel-module-list\n", addr); ---*/ return 1; } #endif /*--- #if defined(CONFIG_CHECK_TIMER_ON_FREED_MODULE) ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned long module_alloc_size_list_alloc(unsigned long size, char *name, enum _module_alloc_type_ type) { unsigned int i; if(type == module_alloc_type_init) return 0L; if((module_alloc_size_list_size == 0) || (module_alloc_size_list_base == 0)) return 0L; if(!module_is_whitelisted(name)) { return 0L; } /*--- printk(KERN_ERR "[module-alloc-by-name] next base is 0x%lx, rest size is 0x%x\n", module_alloc_size_list_base, module_alloc_size_list_size); ---*/ size = ((size + PAGE_SIZE) & ~(PAGE_SIZE - 1)); mutex_lock(&kernel_module_mutex); for(i = 0 ; i < ARRAY_SIZE(module_alloc_size_list); i++) { if(!strcmp(module_alloc_size_list[i].name, name)) { /*--- name gefunden ---*/ if(module_alloc_size_list[i].type == type) { DBG_TRC("[module-alloc-by-name] module '%s' addr %lx found\n", name, module_alloc_size_list[i].addr); if(atomic_read(&module_alloc_size_list[i].alloc)) { DBG_TRC("[module-alloc-by-name] module '%s' already allocated\n", name); mutex_unlock(&kernel_module_mutex); return 0UL; } break; } } if(module_alloc_size_list[i].name[0] == '\0') { snprintf(module_alloc_size_list[i].name, sizeof(module_alloc_size_list[i].name), "%s", name); module_alloc_size_list[i].type = type; size += PAGE_SIZE; /*--- inkl. Reserve-Page fuer Entwicklung -> groesseren Treiber nachladen ---*/ DBG_TRC("[module-alloc-by-name] new module '%s' will use entry %d\n", name, i); break; } } if(i == ARRAY_SIZE(module_alloc_size_list)) { printk(KERN_ERR "[module-alloc-by-name] module alloc table full\n"); mutex_unlock(&kernel_module_mutex); return 0UL; } /*--- wenn noch keine addresse, dann festlegen ---*/ if(module_alloc_size_list[i].addr == 0) { if(size > module_alloc_size_list_size) { module_alloc_size_list[i].name[0] = '\0'; printk(KERN_ERR "[module-alloc-by-name] no kernel-space for module '%s' (0x%lx bytes) -> use ksseg\n", name, size); mutex_unlock(&kernel_module_mutex); return 0UL; } module_alloc_size_list[i].size = size; module_alloc_size_list[i].addr = module_alloc_size_list_base; module_alloc_size_list_size -= size; module_alloc_size_list_base += size; printk(KERN_ERR "[module-alloc-by-name] give 0x%lx bytes at 0x%lx to module '%s' (0x%x bytes left)\n", module_alloc_size_list[i].size, module_alloc_size_list[i].addr, module_alloc_size_list[i].name, module_alloc_size_list_size ); if(avm_kernel_module_memory_config) { struct _avm_kernel_module_memory_config *CMod = avm_kernel_module_memory_config; while(CMod->name) { if(!strcmp(CMod->name, module_alloc_size_list[i].name)) { /*--- printk(KERN_ERR "[module-alloc-by-name] 0x%lx bytes used, 0x%x bytes expected\n", module_alloc_size_list[i].size, CMod->size); ---*/ break; } CMod++; } } } /*--- segment groesse überprüfen ---*/ if(module_alloc_size_list[i].size < size) { printk(KERN_ERR "[module-alloc-by-name] invalid size change 0x%lx bytes < 0x%lx bytes (module '%s')\n", module_alloc_size_list[i].size, size, module_alloc_size_list[i].name); mutex_unlock(&kernel_module_mutex); return 0UL; } atomic_set(&module_alloc_size_list[i].alloc, 1); mutex_unlock(&kernel_module_mutex); return module_alloc_size_list[i].addr; }