/** * 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 #include #include #include static unsigned long module_alloc_size_list_base; static unsigned int module_alloc_size_list_size; static DEFINE_SPINLOCK(kernel_module_lock); /*--- #define DBG_TRC(args...) pr_err(args) ---*/ #define DBG_TRC(args...) no_printk(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_white_list[] = { "pcmlink", "avm_dect", "capi_codec", "isdn_fbox_fon5", "rtc_avm", "krtp", "ulpcmlink", "nlaudio", "essedma", 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); } /** */ 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 kernel-bootmem * es wird ein module_memory-pool 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 memblock_reserve_modulemem(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 += reserved_module_size(CMod); } CMod++; } } else { module_alloc_size_list_size = 4 << 20; pr_err("[module-alloc-by-name] warning fix module_alloc_size_list_size used - todo\n"); } if (module_alloc_size_list_size == 0UL) { pr_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; pr_err("[module-alloc] (start 0x%x end 0x%x) virt=0x%lx\n", module_param.start, module_param.end, aligned_start_addr); if (request_resource(&iomem_resource, &module_param)) { pr_err("[module-alloc] failed 0x%x bytes at 0x%lx\n", module_alloc_size_list_size, aligned_start_addr); module_param.start = 0; module_param.end = 0; return; } memblock_reserve(module_param.start, module_param.end - module_param.start + 1); module_alloc_size_list_base = aligned_start_addr; 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); } } /** */ void __init memblock_modulemem(phys_addr_t *start, phys_addr_t *end) { *start = module_param.start; *end = module_param.end; } /** */ char *module_alloc_find_module_name(char *buff, char *end, unsigned long addr) { unsigned int i; unsigned int len; spin_lock_bh(&kernel_module_lock); 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); spin_unlock_bh(&kernel_module_lock); return buff + len; } spin_unlock_bh(&kernel_module_lock); 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; spin_lock_bh(&kernel_module_lock); 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); spin_unlock_bh(&kernel_module_lock); return 0; } } } DBG_TRC("[%s] pointer 0x%lx not found in kernel-module-list\n", __func__, addr); spin_unlock_bh(&kernel_module_lock); 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 kernel-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 (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; } /*--- pr_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_special_page_aligned(size); spin_lock_bh(&kernel_module_lock); 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); spin_unlock_bh(&kernel_module_lock); 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)) { pr_err("[module-alloc-by-name] module alloc table full\n"); spin_unlock_bh(&kernel_module_lock); 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 kernel-space for module '%s' (0x%lx bytes) -> use ksseg\n", name, size); spin_unlock_bh(&kernel_module_lock); 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 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: warning: 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 (module_alloc_size_list[i].size < 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); spin_unlock_bh(&kernel_module_lock); return 0UL; } atomic_set(&module_alloc_size_list[i].alloc, 1); spin_unlock_bh(&kernel_module_lock); 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_puts(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_puts(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);