最近在测试某些嵌入式设备,在研究过程中需要在系统启动后替换m25p80驱动以达到特定效果。而该设备固件将m25p80直接编译到内核中,并非以LKM的形式存在,无法使用rmmod卸载。
查阅资料后发现,内核在处理spi相关驱动的时候,使用spi_register_driver
函数注册m25p80驱动,并且提供spi_unregister_driver
函数用于卸载驱动。进一步查看这两个函数的定义,可以发现主要参数都是一个spi_driver
结构体。
1 2 int __spi_register_driver(struct module *owner, struct spi_driver *sdrv) void spi_unregister_driver(struct spi_driver *sdrv)
查看m25p80.c的代码,其在尾部定义了spi_driver
结构体,名称为m25p80_driver
,并使用module_spi_driver
宏注册驱动。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static struct spi_driver m25p80_driver = { .driver = { .name = "m25p80", .of_match_table = m25p_of_table, }, .id_table = m25p_ids, .probe = m25p_probe, .remove = m25p_remove, /* REVISIT: many of these chips have deep power-down modes, which * should clearly be entered on suspend() to minimize power use. * And also when they're otherwise idle... */ }; MODULE_LICENSE("GPL"); module_spi_driver(m25p80_driver);
进一步确定目标设备固件的/proc/kallsyms中有m25p80_driver
的地址,这就是我们需要的spi_driver
结构体。
在确定上述信息后,编写一个LKM,作用是调用spi_unregister_driver
函数来卸载内核中的m25p80驱动,卸载后即可insmod加载我们自定义的m25p80驱动,达到效果。
LKM关键代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 static char *m25p80_addr = NULL; module_param(m25p80_addr, charp, 0000); MODULE_PARM_DESC(m25p80_addr, "m25p80_driver addr"); static int __init hello_start(void) { unsigned long m25p80_dri; if ( m25p80_addr != NULL && kstrtoul(m25p80_addr, 16, (unsigned long *)&m25p80_dri)==0 ) { spi_unregister_driver((struct spi_driver *)(uintptr_t)m25p80_dri); printk("unload m25p80 done\n"); } else { printk("unload m25p80 failed\n"); } return 0; }