需求:我的网卡驱动在加载时,需要两个elf格式的固件。SDK的做法是将这两个固件放置到跟文件系统中的/lib/firmware目录下,内核启动的时候在根文件系统中寻找。这样做,内核和根文件系统就出现了一个强相关的关系。客户提出,在内核不要和根文件系统较强联系,不要出现这种情况:编译出的kernel和此根文件系统启动后,OK没有问题!!!而换了一个根文件系统,居然网络都不能用了!!!
根据客户的需求,我将SDK的elf固件编译进内核。
那么问题来了。。。如何elf固件编译进内核??
打开内核源码,在顶层目录中,有一个名为firmware的目录,对就是它就是那个固件目录。 在此目录下,有很多hex文件,这些文件都不是源码文件,那这些文件是做什么用的楠?
阅读/firmware/Makefile,我们可以知道kernel中已有的编译固件到内核的方法是(如dd.fw.hex):通过dd.fw文件生成相应二进制固件文件,将对应二进制文件通过quiet_cmd_fwbin规则包含进dd.fw.S文件,将dd.fw.S文件编译成对应的dd.fw.o文件,链接进内核镜像中。
那么这下来将两个elf文件编译进内核(a.elf, b.elf)。理清了上面原理,我们可以很容易的将elf文件编译进内核。
fw-shipped-y = a.elf , b.elf通过上面的一行makefile语句,将a.elf b.elf编译进内核。
到这里就接受了么? 你会发现一个奇怪的问题:每次make ARCH=xxx CROSS_COMPILE=xxx clean 或者distclean后,这两个elf都会被删除!再次编译编译失败:a.elf not found。 因为我们将之定义为目标,在每次make clean时都会将生成目标全部删除,内核中其他的目标都是通过ihex文件生成,所以删除了无关紧要,而我们的elf文件,删除了就没有了。a.elf -> a.o 过程依赖a.elf。a.elf文件不存在出现编译失败! 解决的方法很简单:clean 或 distclean 不将elf文件删除! 在/firmware/Makefile文件添加下面行:
no-clean-fils=a.elf b.elf现在clean 或 distclean时,将不会删除elf文件。
我们再次深入,你将会发现还有问题:make ARCH=xxx CROSS_COMPILE=xxx O=$(dir) 编译失败,错误还是为:a.elf b.elf not found (解决此问题需要对此Makefile有一定的深入了解)
a.elf 编译进内核的路径为:a.elf -> a.S -> a.o -> vmlinux dd.fw 编译进内核的路径为:dd.fw.ihex -> dd.fw -> dd.fw.S -> dd.fw.o -> vmlinux 这两个文件编译进内核的方式,除了第一步就没有其他的区别!对!就是第一步导致编译的失败! make 的O= (dir)的意思是:将make过程中的中间文件都到 (dir)目录下。从a.elf到a.S通过quiet_cmd_fwbin规则实现!.S文件中使用汇编伪指令.incbin将elf文件直接包含到.S文件中。只要你打开.S文件你就明白了这里为什么会出错了。使用了相对路径!!!而elf文件在源码路径,而不在obj路径中,所以相对路径下找不到elf文件导致编译失败。(大家想想为什么dd.fw 又没有问题?)。 给出我的修改方法,仅供参考:
quiet_cmd_fwbin = MK_FW $@ cmd_fwbin = FWNAME="$(patsubst firmware/%.gen.S,%,$@)"; \ FWSTR="$(subst /,_,$(subst .,_,$(subst -,_,$(patsubst \ firmware/%.gen.S,%,$@))))"; \ ASM_WORD=$(if $(CONFIG_64BIT),.quad,.long); \ ASM_ALIGN=$(if $(CONFIG_64BIT),3,2); \ PROGBITS=$(if $(CONFIG_ARM),%,@)progbits; \ echo "/* Generated by firmware/Makefile */" > $@;\ echo " .section .rodata" >>$@;\ echo " .p2align $${ASM_ALIGN}" >>$@;\ echo "_fw_$${FWSTR}_bin:" >>$@;\ echo " .incbin \"<$(srctree)/$(2)\"" >>$@;\ echo "_fw_end:" >>$@;\ echo " .section .rodata.str,\"aMS\",$${PROGBITS},1" >>$@;\ echo " .p2align $${ASM_ALIGN}" >>$@;\ echo "_fw_$${FWSTR}_name:" >>$@;\ echo " .string \"$$FWNAME\"" >>$@;\ echo " .section .builtin_fw,\"a\",$${PROGBITS}" >>$@;\ echo " .p2align $${ASM_ALIGN}" >>$@;\ echo " $${ASM_WORD} _fw_$${FWSTR}_name" >>$@;\ echo " $${ASM_WORD} _fw_$${FWSTR}_bin" >>$@;\ echo " $${ASM_WORD} _fw_end - _fw_$${FWSTR}_bin" >>$@;note: linux kernel中: (srctree)表示内核源码目录。 (objtree)表示内核编译生成目录,包括中间文件。
