#include #include #include #include #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(unmapped_module_mutex); /*--- #define DBG_TRC(args...) pr_err( args) ---*/ #define DBG_TRC(args...) /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ 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_black_list[] = { /*--- "avm_coredump", "flash_update", "kspeedtest", ---*/ NULL }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int module_is_blacklisted(char *name) { char **p; p = module_load_black_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) { return (addr >= (unsigned long)__va(module_param.start)) && (addr < (unsigned long)__va(module_param.end)); } /** */ static unsigned int size_special_page_aligned(unsigned int size){ return ((size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)); } /** */ static unsigned int reserved_module_size(struct _avm_kernel_module_memory_config *CMod){ #if defined(CONFIG_KALLSYMS) #if defined(CONFIG_KALLSYMS_ALL) return size_special_page_aligned(CMod->core_size + CMod->symbol_size); #else/*--- #if defined(CONFIG_KALLSYMS_ALL) ---*/ return size_special_page_aligned(CMod->core_size + CMod->symbol_text_size); #endif/*--- #else ---*//*--- #if defined(CONFIG_KALLSYMS_ALL) ---*/ #else/*--- #if defined(CONFIG_KALLSYMS) ---*/ return size_special_page_aligned(CMod->core_size); #endif/*--- #else ---*//*--- #if defined(CONFIG_KALLSYMS) ---*/ } /*--------------------------------------------------------------------------------*\ * Initiale Vorbereitungen fuer unmapped-memory-bootmem * es wird ein extra module_memory reserviert, so dass Treiber direkt in unmapped Speicher * geladen werden koennen. * Dies wird z.B. unbedingt benoetigt, falls Module die Yield-Schnittstelle verwenden * will (es duerfen im Yield-Kontext keine TLB-Exception auftreten!) \*--------------------------------------------------------------------------------*/ void __init module_alloc_bootmem_init(struct resource *res) { unsigned int i, module_size; char *arg; 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_blacklisted(CMod->name)) { module_size = reserved_module_size(CMod); module_alloc_size_list_size += module_size; DBG_TRC("%s: module '%s' size=%u\n", __func__, CMod->name, module_size); } CMod++; } } else if ((arg = prom_getenv("modulemem")) != NULL) { module_alloc_size_list_size = simple_strtoul(arg, NULL, 0) + (ARRAY_SIZE(module_alloc_size_list) * PAGE_SIZE); pr_err( "[module-alloc-by-name] warning 'modulemem' used - depreciated\n"); } if (module_alloc_size_list_size == 0UL) { pr_err( "[module-alloc-by-name] 'modulemem' not set, " "function disabled\n"); return; } module_alloc_size_list_base = (unsigned long)alloc_bootmem_pages(module_alloc_size_list_size + (1 << PAGE_SHIFT)); if(module_alloc_size_list_base == 0L) { pr_err( "[module-alloc] reserve memory for module-load failed (start 0x%x end 0x%x)\n", module_param.start, module_param.end); module_alloc_size_list_size = 0; return; } module_param.start = CPHYSADDR(module_alloc_size_list_base); module_param.end = CPHYSADDR(module_alloc_size_list_base + module_alloc_size_list_size); module_param.name = "module memory"; module_param.flags = IORESOURCE_MEM | IORESOURCE_BUSY; if (request_resource(res, &module_param)) { pr_err( "[module-alloc] failed 0x%x bytes at 0x%lx\n", module_alloc_size_list_size, module_alloc_size_list_base); } else { pr_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; 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); return buff + len; } 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(&unmapped_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 únmapped-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(&unmapped_module_mutex); return 0; } } } DBG_TRC("[%s] pointer 0x%lx not found in unmapped-memory-module-list\n", __func__, addr); mutex_unlock(&unmapped_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; } /*--- pr_err( "[module-alloc-by-name] pointer 0x%lx isn't in unmapped-module-list\n", addr); ---*/ return 1; } #endif /*--- #if defined(CONFIG_CHECK_TIMER_ON_FREED_MODULE) ---*/ static unsigned long module_alloc_get_reserved_size_by_name(const char *name) { // Work around for odd led module naming if (strncmp(name, "led", strlen("led")) == 0) { name = "led_module"; } 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, name)) { return reserved_module_size(CMod); } CMod++; } } return 0; } static int module_alloc_waste; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned long module_alloc_size_list_alloc(unsigned long size, char *name, enum _module_alloc_type_ type) { unsigned long aligned_module_size; unsigned int i; if((module_alloc_size_list_size == 0) || (module_alloc_size_list_base == 0)) return 0L; if(module_is_blacklisted(name)) { pr_err( "[module-alloc-by-name] module '%s' on black list, use normal alloc\n", name); return 0UL; } DBG_TRC("[%s] next base is 0x%lx, rest size is 0x%x\n", __func__, module_alloc_size_list_base, module_alloc_size_list_size); size = size_special_page_aligned(size); mutex_lock(&unmapped_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("[%s] module '%s' addr %lx found\n", __func__, 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(&unmapped_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; DBG_TRC("[%s] new module '%s' will use entry %d\n", __func__, name, i); break; } } if(i == ARRAY_SIZE(module_alloc_size_list)) { pr_err( "[module-alloc-by-name] module alloc table full\n"); mutex_unlock(&unmapped_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'; pr_err("[module-alloc-by-name] no unmapped-space for module '%s' (0x%lx bytes) -> use ksseg\n", name, size); mutex_unlock(&unmapped_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; aligned_module_size = module_alloc_get_reserved_size_by_name(module_alloc_size_list[i].name); pr_err( "[module-alloc-by-name] give 0x%lx bytes at 0x%lx to module '%s' (0x%x total 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(aligned_module_size > size) { module_alloc_waste += aligned_module_size - size; pr_err("%s: error: module '%s' reserved size %lu is to great for demand size %lu - waste %lu" " (module_alloc_waste=%d)\n", __func__, module_alloc_size_list[i].name, aligned_module_size, size, aligned_module_size - size, module_alloc_waste); } else if(size > aligned_module_size) { module_alloc_waste -= size - aligned_module_size; pr_err("%s: error: module '%s' reserved size %lu too small for demand size %lu - need %lu more" " (module_alloc_waste=%d)\n", __func__, module_alloc_size_list[i].name, aligned_module_size, size, size - aligned_module_size, module_alloc_waste); } } /*--- segment groesse überprüfen ---*/ if(size > module_alloc_size_list[i].size) { pr_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(&unmapped_module_mutex); return 0UL; } atomic_set(&module_alloc_size_list[i].alloc, 1); mutex_unlock(&unmapped_module_mutex); return module_alloc_size_list[i].addr; } static void module_alloc_proc_allocated(struct seq_file *file, void *ctx) { int i; unsigned long allocated = 0; seq_printf(file, "%30s %15s %15s\n", "Module", "Allocated", "Reserved"); for(i = 0 ; i < ARRAY_SIZE(module_alloc_size_list); i++) { struct _module_alloc_size_list* mod = module_alloc_size_list + i; unsigned long reserved; // End of allocated list if (!*mod->name) { break; } allocated += mod->size; seq_printf(file, "%30s %15ld", mod->name, mod->size); reserved = module_alloc_get_reserved_size_by_name(mod->name); if (reserved == 0) { seq_printf(file, " %15s", "-"); } else { seq_printf(file, " %15ld", reserved); } seq_printf(file, "\n"); } seq_printf(file, "\n Total: %8ld\nAllocated: %8ld\n Reserved: %8d\n", module_alloc_size_list_size + allocated, allocated, module_alloc_size_list_size); } static void module_alloc_proc_reserved(struct seq_file *file, void *ctx) { struct _avm_kernel_module_memory_config *mod; if(!avm_kernel_module_memory_config) { seq_printf(file, "No module memory config found!\n"); return; } seq_printf(file, "%30s %15s %15s %15s\n", "Module", "Reserved", "CoreSize", "SymbolSize"); for (mod = avm_kernel_module_memory_config; mod->name; mod++) { seq_printf(file, "%30s %15d %15d %15d\n", mod->name, reserved_module_size(mod), mod->core_size, #if defined(CONFIG_KALLSYMS_ALL) mod->symbol_size #else/*--- #if defined(CONFIG_KALLSYMS_ALL) ---*/ mod->symbol_text_size #endif/*--- #else ---*//*--- #if defined(CONFIG_KALLSYMS_ALL) ---*/ ); } } static __init int module_alloc_proc_init(void) { proc_mkdir("avm/module", NULL); add_simple_proc_file("avm/module/allocated", NULL, module_alloc_proc_allocated, NULL); add_simple_proc_file("avm/module/reserved", NULL, module_alloc_proc_reserved, NULL); return 0; } late_initcall(module_alloc_proc_init);