解密module_init

本文详细探讨了Linux环境下模块初始化函数module_init的工作原理。分别介绍了该宏在内核编译时与动态加载时的不同实现方式,并解释了初始化函数的具体调用时机。

 http://blog.163.com/xinbuqianjin@126/blog/static/167563447201010221231507/

在Linux底下写过driver模块的对这个宏一定不会陌生。module_init宏在MODULE宏有没有定义的情况下展开的内容是不同的,如果这个宏没有定义,基本上表明阁下的模块是要编译进内核的(obj-y)。

1.在MODULE没有定义这种情况下,module_init定义如下:

#define module_init(x) __initcall(x);

因为

#define __initcall(fn)                            device_initcall(fn)

#define device_initcall(fn) __define_initcall("6",fn,6)

#define __define_initcall(level,fn,id) \

static initcall_t __initcall_##fn##id __used \

__attribute__((__section__(".initcall" level ".init"))) = fn

所以,module_init(x)最终展开为:

static initcall_t __initcall_##fn##id __used \

__attribute__((__section__(".initcall" level ".init"))) = fn

更直白点,假设阁下driver所对应的模块的初始化函数为int gpio_init(void),那么module_init(gpio_init)实际上等于:

static initcall_t  __initcall_gpio_init_6 __used __attribute__((__section__(".initcall6.init"))) = gpio_init;

就是声明一类型为initcall_t(typedef int (*initcall_t)(void))函数指针类型的变量__initcall_gpio_init_6并将gpio_init赋值与它。

这里的函数指针变量声明比较特殊的地方在于,将这个变量放在了一名为".initcall6.init"节中。接下来结合vmlinux.lds中的

.initcall.init : AT(ADDR(.initcall.init) - (0xc0000000 -0x00000000)) {

   __initcall_start = .;

   *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)

   __initcall_end = .;

   }

以及do_initcalls:

static void __init do_initcalls(void)

{

initcall_t *call;

for (call = __initcall_start; call < __initcall_end; call++)

do_one_initcall(*call);

/* Make sure there is no pending stuff from the initcall sequence */

flush_scheduled_work();

}

那么就不难理解阁下模块中的module_init中的初始化函数何时被调用了:在系统启动过程中start_kernel()->rest_init()->kernel_init()->do_basic_setup()->do_initcalls()。

 

2.在MODULE被定义的情况下(大部分可动态加载的driver模块都属于此, obj-m),module_init定义如下:

#define module_init(initfn) \

static inline initcall_t __inittest(void) \

{ return initfn; } \

int init_module(void) __attribute__((alias(#initfn)));

这段宏定义关键点是后面一句,通过alias将initfn变名为init_module。前面那个__inittest的定义其实是种技巧,用来对initfn进行某种静态的类型检查,如果阁下将模块初始化函数定义成,比如,void gpio_init(void)或者是int gpio_init(int),那么在编译时都会有类似下面的warning:

GPIO/fsl-gpio.c: In function '__inittest':

GPIO/fsl-gpio.c:46: warning: return from incompatible pointer type

通过module_init将模块初始化函数统一别名为init_module,这样以后insmod时候,在系统内部会调用sys_init_module()去找到init_module函数的入口地址。

如果objdump -t gpio.ko,就会发现init_module和gpio_init位于相同的地址偏移处。简言之,这种情况下模块的初始化函数在insmod时候被调用。

/* * Copyright 2019 TP-Link Technologies Co., Ltd. All rights reserved. * * */ #include "ship_module.h" #include "ship_model_conf.h" #include "ship_partition.h" static ship_module_t **sys_modules = NULL; static SH_INT32 module_num = 0; static SH_INT32 module_num_max = 0; static SH_UINT8 dset_buf[SHIP_USER_CONFIG_BUF_SIZE]; static osal_mutex_t mod_dset_lock; static SH_UINT8 ship_module_aes256_key[32] = {0}; static SH_UINT8 ship_module_aes256_iv[16] = {0}; void ship_module_register(ship_module_t* module) { ship_module_t **new_ptr; if (!sys_modules) { sys_modules = osal_malloc(sizeof(ship_module_t*) * 10); module_num_max = 10; if (!sys_modules) return; } if (module_num >= module_num_max) { /* space if not enough, need realloc */ new_ptr = osal_malloc(sizeof(ship_module_t*) * (module_num_max + 5)); if (!new_ptr) return; OSAL_MEMCPY(new_ptr, sys_modules, sizeof(ship_module_t*) * module_num_max); osal_free(sys_modules); sys_modules = new_ptr; module_num_max += 5; } sys_modules[module_num++] = module; } void ship_module_run_init_all() { SH_INT32 i; for (i = 0; i < module_num; i++) { if (sys_modules[i]->ops->init) sys_modules[i]->ops->init(); } } void ship_module_run_start_all() { SH_INT32 i; for (i = 0; i < module_num; i++) { if (sys_modules[i]->ops->start) sys_modules[i]->ops->start(); } } void ship_module_run_erase_wifi_all() { SH_INT32 i; for (i = 0; i < module_num; i++) { if (sys_modules[i]->ops->erase_wifi_cb) sys_modules[i]->ops->erase_wifi_cb(); } } SH_BOOL ship_module_reset_check(SH_CHAR *module_name, json_t *filterArr) { SH_INT32 array_size, i; json_t *item; if (NULL == filterArr) return TRUE; if (NULL == module_name) return FALSE; array_size = json_get_array_size(filterArr); for (i = 0; i < array_size; i++) { item = json_get_array_item(filterArr, i); if (!OSAL_STRCMP(module_name, item->valuestring)) return TRUE; } return FALSE; } void ship_module_run_reset_all(json_t *filterArr) { SH_UINT32 i; /* disable factory mode when do factory reset */ if (filterArr == NULL) { if (ship_model_conf_get_factory_mode()) ship_model_conf_set_factory_mode(FALSE); } for (i = 0; i < module_num; i++) { if (sys_modules[i]->ops->reset && ship_module_reset_check(sys_modules[i]->name, filterArr)) { SHIP_D_PRINTF("rst module:%s\n", sys_modules[i]->name); sys_modules[i]->ops->reset(); } } } static ship_module_t* get_module_by_name(SH_CHAR *name) { SH_INT32 i; for (i = 0; i < module_num; i++) { if (!OSAL_STRNCMP(sys_modules[i]->name, name, SHIP_MOD_NAME_LEN)) // hard code a module name length here return sys_modules[i]; } return NULL; } void ship_module_run_load_config() { SH_UINT8 *ptr; SH_UINT32 size, i, offset = 0, csum = 0, config_len; ship_module_t* pm; ship_modconfig_head_t *phead, mhead; ship_userconfig_head_t *uhead; /* load header and encrypted data */ OSAL_MEMZERO(dset_buf, sizeof(dset_buf)); ship_partition_read(SHIP_PARTITION_USER_CONFIG, 0, dset_buf, sizeof(dset_buf)); ptr = dset_buf; uhead = (ship_userconfig_head_t*)ptr; /* step1: header validation */ ptr += ALIGN_4B(sizeof(ship_userconfig_head_t)); if (uhead->signature != SHIP_USERCONFIG_MAGIC) { SHIP_D_PRINTF("magic invalid\n"); uhead->reset_flag = TRUE; goto reset_all; } if (uhead->version >= SHIP_USERCONFIG_MIN_VERSION) { for (i = 0; i < uhead->encrypted_length; i++) csum += ptr[i]; if (csum != uhead->checksum) { SHIP_D_PRINTF("checksum invalid\n"); uhead->reset_flag = TRUE; goto reset_all; } } else { SHIP_D_PRINTF("version invalid\n"); uhead->reset_flag = TRUE; goto reset_all; } /* step2: parse encrypted data */ if (uhead->version >= SHIP_USERCONFIG_MIN_ENCRYPT_VERSION) { osal_aes256_cbc_dec(ptr, uhead->encrypted_length, ship_module_aes256_key, sizeof(ship_module_aes256_key), ship_module_aes256_iv, ptr); } size = uhead->encrypted_length; while (size >= sizeof(ship_modconfig_head_t)){ phead = (ship_modconfig_head_t *)ptr; if (phead->name[0] == 0){ // null name means end of TLV break; } SHIP_D_PRINTF("load mod %s\n", phead->name); if (phead->len > size) { // illegal size, maybe content is broken SHIP_D_PRINTF("illegal len:%d\n", phead->len); break; } if (((pm = get_module_by_name(phead->name)) == NULL) || pm->cfg_type != SHIP_USER_CONFIG_JSON){ // skip unknown mod cfg size -= ALIGN_4B(sizeof(ship_modconfig_head_t) + phead->len); ptr += ALIGN_4B(sizeof(ship_modconfig_head_t) + phead->len); continue; } if (size < sizeof(ship_modconfig_head_t) + phead->len){ break; } // make sure null terminate phead->value[phead->len - 1] = 0; pm->ops->load_config(phead->value); pm->cfg_loaded = TRUE; size -= ALIGN_4B(sizeof(ship_modconfig_head_t) + phead->len); ptr += ALIGN_4B(sizeof(ship_modconfig_head_t) + phead->len); } /* step3: parse non-encrypted data */ size = uhead->length - ALIGN_4B(uhead->encrypted_length); offset = ALIGN_4B(sizeof(ship_userconfig_head_t)) + ALIGN_4B(uhead->encrypted_length); while (size >= sizeof(ship_modconfig_head_t)){ ship_partition_read(SHIP_PARTITION_USER_CONFIG, offset, (SH_UINT8*)&mhead, sizeof(mhead)); if (mhead.name[0] == 0){ // null name means end of TLV break; } SHIP_D_PRINTF("load mod %s\n", mhead.name); if (mhead.len > size) { // illegal size, maybe content is broken SHIP_D_PRINTF("illegal len:%d\n", mhead.len); break; } if (((pm = get_module_by_name(mhead.name)) == NULL) || pm->cfg_type != SHIP_USER_CONFIG_RAW){ // skip unknown mod cfg SHIP_D_PRINTF("unknown mod %s\n", mhead.name); size -= ALIGN_4B(sizeof(ship_modconfig_head_t) + mhead.len); offset += ALIGN_4B(sizeof(ship_modconfig_head_t) + mhead.len); continue; } if (size < sizeof(ship_modconfig_head_t) + mhead.len){ break; } ptr = pm->ops->get_config_ptr(&config_len); if (config_len == mhead.len){ ship_partition_read(SHIP_PARTITION_USER_CONFIG, offset + sizeof(ship_modconfig_head_t), ptr, config_len); pm->cfg_loaded = TRUE; } else { SHIP_D_PRINTF("mod %s length invalid %d %d\n", mhead.name, mhead.len, config_len); } size -= ALIGN_4B(sizeof(ship_modconfig_head_t) + mhead.len); offset += ALIGN_4B(sizeof(ship_modconfig_head_t) + mhead.len); } reset_all: for (i = 0; i < module_num; i++) { if (sys_modules[i]->cfg_type != SHIP_USER_CONFIG_NONE && !sys_modules[i]->cfg_loaded){ SHIP_D_PRINTF("reset %s\n", sys_modules[i]->name); if (sys_modules[i]->ops->reset) sys_modules[i]->ops->reset(); sys_modules[i]->cfg_loaded = TRUE; } } } static void _ship_module_run_save_config(SH_BOOL reset_flag) { SH_UINT8 *ptr, *cfg; SH_UINT32 size, offset, cur_index = 0, encrypted_length = 0, csum = 0, i, config_len; ship_module_t* pm; ship_modconfig_head_t *phead, mhead; ship_userconfig_head_t *uhead; //SH_UINT64 startTime = osal_time_ms(); //SHIP_D_PRINTF("***********\r\nThe save start time is %lld\r\n", startTime); /* step1: prepare header, but checksum and length yet to be done */ ptr = dset_buf; size = sizeof(dset_buf); uhead = (ship_userconfig_head_t*)ptr; uhead->version = SHIP_USERCONFIG_CUR_VERSION; uhead->signature = SHIP_USERCONFIG_MAGIC; uhead->reset_flag = reset_flag; /* step2: write json data to buffer */ size -= ALIGN_4B(sizeof(ship_userconfig_head_t)); ptr += ALIGN_4B(sizeof(ship_userconfig_head_t)); while (size >= sizeof(ship_modconfig_head_t)) { phead = (ship_modconfig_head_t *)ptr; if (cur_index >= module_num){ phead->name[0] = 0; // end tag encrypted_length += sizeof(ship_modconfig_head_t); break; } pm = sys_modules[cur_index]; if (NULL == pm || pm->cfg_type != SHIP_USER_CONFIG_JSON) cfg = NULL; else cfg = pm->ops->get_config_ptr(&config_len); if(!cfg){ cur_index++; continue; } phead->len = config_len + 1; //OSAL_PRINTF("MODULE:%s\nLENGTH:%d\nDATA:%s\n", pm->name, phead->len, cfg); OSAL_STRNCPY(phead->name, pm->name, SHIP_MOD_NAME_LEN); phead->name[SHIP_MOD_NAME_LEN] = '\0'; if (size < sizeof(ship_userconfig_head_t) + config_len + 1) { osal_free(cfg); break; } OSAL_STRCPY((SH_CHAR*)phead->value, (SH_CHAR*)cfg); cur_index++; size -= ALIGN_4B(sizeof(ship_modconfig_head_t) + phead->len); encrypted_length += ALIGN_4B(sizeof(ship_modconfig_head_t) + phead->len); ptr += ALIGN_4B(sizeof(ship_modconfig_head_t) + phead->len); osal_free(cfg); } /* step3: encrypt json data in the buffer */ /* update total len, make it 16 bytes aligned */ if (encrypted_length % 16 != 0) { OSAL_MEMSET(ptr, 0, 16 - (encrypted_length % 16)); encrypted_length = encrypted_length + 16 - (encrypted_length % 16); } SHIP_D_PRINTF("tl=%d, hl=%d, el=%d\n", (SH_INT32)sizeof(dset_buf), (SH_INT32)ALIGN_4B(sizeof(ship_userconfig_head_t)), encrypted_length); /* aes256 calculation */ ptr = dset_buf + ALIGN_4B(sizeof(ship_userconfig_head_t)); osal_aes256_cbc_enc(ptr, encrypted_length, ship_module_aes256_key, sizeof(ship_module_aes256_key), ship_module_aes256_iv, ptr); /* step4: write raw data to flash */ ship_partition_erase(SHIP_PARTITION_USER_CONFIG); offset = ALIGN_4B(sizeof(ship_userconfig_head_t)) + ALIGN_4B(encrypted_length); for (i = 0; i < module_num; i++) { pm = sys_modules[i]; if (NULL == pm || pm->cfg_type != SHIP_USER_CONFIG_RAW) continue; cfg = pm->ops->get_config_ptr(&config_len); OSAL_MEMZERO(&mhead, sizeof(mhead)); mhead.len = config_len; OSAL_STRNCPY(mhead.name, pm->name, SHIP_MOD_NAME_LEN); ship_partition_write(SHIP_PARTITION_USER_CONFIG, offset, (SH_UINT8*)&mhead, sizeof(mhead)); ship_partition_write(SHIP_PARTITION_USER_CONFIG, offset + sizeof(ship_modconfig_head_t), cfg, config_len); offset += ALIGN_4B(sizeof(ship_modconfig_head_t) + mhead.len); } // null TLV OSAL_MEMZERO(&mhead, sizeof(mhead)); ship_partition_write(SHIP_PARTITION_USER_CONFIG, offset, (SH_UINT8*)&mhead, sizeof(mhead)); offset += ALIGN_4B(sizeof(ship_modconfig_head_t)); /* step5: complete the header and write to flash */ uhead->length = offset - ALIGN_4B(sizeof(ship_userconfig_head_t)); uhead->encrypted_length = encrypted_length; ptr = dset_buf + ALIGN_4B(sizeof(ship_userconfig_head_t)); for (i = 0; i < encrypted_length; i++) csum += ptr[i]; uhead->checksum = csum; ship_partition_write(SHIP_PARTITION_USER_CONFIG, 0, dset_buf, ALIGN_4B(sizeof(ship_userconfig_head_t)) + encrypted_length); //SH_UINT64 endTime = osal_time_ms(); //SHIP_D_PRINTF("The save end time is %lld\r\n", endTime); //SHIP_D_PRINTF("The comsume time is %lld\r\n **************\r\n", endTime-startTime); } typedef struct _ship_nvram_cache_header_t{ SH_UINT32 magicNum; SH_INT32 entry_start_addr[SHIP_SLOT_NUM]; SH_INT32 entry_size[SHIP_SLOT_NUM]; }ship_nvram_cache_header_t; #define SHIP_CONFIG_CACHE_MAGIC 0x43414348 // CACH static SH_INT32 cache_offset[SHIP_SLOT_NUM]; SH_BOOL ship_module_save_data_to_cache(SH_INT32 index, void *data, SH_INT32 len) { ship_nvram_cache_header_t header; SH_INT32 i, size, part_len; ship_partition_read(SHIP_PARTITION_USER_CACHE, 0, (SH_UINT8 *)&header, sizeof(header)); if (header.magicNum != SHIP_CONFIG_CACHE_MAGIC) { ship_partition_erase(SHIP_PARTITION_USER_CACHE); /* check if erase success */ ship_partition_read(SHIP_PARTITION_USER_CACHE, 0, (SH_UINT8 *)&header, sizeof(header)); if (header.magicNum != 0xFFFFFFFF){ /* some platform cache is shared with other partition, so can not really erase OSAL_NVRAM_USER_CACHE */ return FALSE; } part_len = ship_partition_get_size(SHIP_PARTITION_USER_CACHE); size = (part_len - sizeof(header)) / SHIP_SLOT_NUM; for (i = 0; i < SHIP_SLOT_NUM; i++) { header.entry_start_addr[i] = sizeof(header) + size * i; header.entry_size[i] = size; cache_offset[i] = 0; } header.magicNum = SHIP_CONFIG_CACHE_MAGIC; ship_partition_write(SHIP_PARTITION_USER_CACHE, 0, (SH_UINT8 *)&header, sizeof(header)); } if (cache_offset[index] + len > header.entry_size[index]) { SHIP_D_PRINTF("cache full\n"); return FALSE; } ship_partition_write(SHIP_PARTITION_USER_CACHE, header.entry_start_addr[index] + cache_offset[index], data, len); cache_offset[index] += len; return TRUE; } static SH_BOOL cache_data_validate(SH_UINT8 *data, SH_INT32 len) { SH_INT32 i; /* all 0xFF means invalid */ for (i = 0; i < len; i++) { if (data[i] != 0xFF) return TRUE; } return FALSE; } SH_BOOL ship_module_read_data_from_cache(SH_INT32 index, void *data, SH_INT32 len) { ship_nvram_cache_header_t header; cache_offset[index] = 0; ship_partition_read(SHIP_PARTITION_USER_CACHE, 0, (SH_UINT8 *)&header, sizeof(header)); if (header.magicNum != SHIP_CONFIG_CACHE_MAGIC) { SHIP_D_PRINTF("Read cache fail\n"); return FALSE; } while (cache_offset[index] + len <= header.entry_size[index]) { ship_partition_read(SHIP_PARTITION_USER_CACHE, header.entry_start_addr[index] + cache_offset[index], (SH_UINT8*)data, len); if (!cache_data_validate((SH_UINT8*)data, len)) { if (cache_offset[index] >= len) { /* if current block is invalid, return last valid block */ ship_partition_read(SHIP_PARTITION_USER_CACHE, header.entry_start_addr[index] + cache_offset[index] - len, (SH_UINT8*)data, len); return TRUE; } else { /* first block is invalid */ return FALSE; } } cache_offset[index] += len; } /* cache is full, return the last block */ if (cache_offset[index] + len > header.entry_size[index]) { ship_partition_read(SHIP_PARTITION_USER_CACHE, header.entry_start_addr[index] + cache_offset[index] - len, (SH_UINT8*)data, len); return TRUE; } cache_offset[index] = 0; return FALSE; } void ship_module_run_save_config() { _ship_module_run_save_config(FALSE); } void ship_module_run_save_config_locked() { ship_module_dset_lock(); ship_module_run_save_config(); ship_module_dset_unlock(); } void ship_module_run_save_rst_config() { _ship_module_run_save_config(TRUE); } void ship_module_dset_lock_init() { if (osal_mutex_create(&(mod_dset_lock)) != OSAL_OK) { SHIP_D_PRINTF("mod_dset_lock create fail\n"); } } void ship_module_dset_lock() { osal_mutex_lock(mod_dset_lock); } void ship_module_dset_unlock() { osal_mutex_unlock(mod_dset_lock); } SH_BOOL ship_module_is_factory() { ship_userconfig_head_t uhead; ship_partition_read(SHIP_PARTITION_USER_CONFIG, 0, (SH_UINT8*)&uhead, sizeof(uhead)); return uhead.reset_flag; } void ship_module_aes256_init() { SH_CHAR deviceId[41] = {0}; SH_UINT8 mac[6] = {0}; // generate iv according to mac address ship_model_conf_get_device_mac(mac); OSAL_MEMSET(ship_module_aes256_iv, 0, sizeof(ship_module_aes256_iv)); OSAL_MEMCPY(ship_module_aes256_iv, mac, sizeof(mac)); // generate aes256 key ship_model_conf_get_device_id(deviceId, 40); osal_sha256_digest((SH_UINT8*)deviceId, 40, ship_module_aes256_key); } 这个呢
最新发布
12-09
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值