作者:付汉杰,hankf@xilinx.com,文章转载自:赛灵思中文社区论坛
创建内核模块
Petalinux可以帮助工程师简化内核模块的创建工作。在petalinux工程目录下,使用命令“petalinux-create -t modules --name --enable”,能创建Linux内核模块,包括c源代码文件、Makefile、Yocto的bb文件。相关文件放在目录“project-spec/meta-user/recipes-modules”,目录结构如下。
hankf@XSZGS4:~/proj/vcu-trd-2020.2-peta-qt/project-spec/meta-user/recipes-modules$ tree . └── linux-test-module ├── files │ ├── COPYING │ ├── linux-test-module.c │ └── Makefile ├── linux-test-module.bb └── README 2 directories, 5 files
Makefile
原始Makefile
原始的Makefile只支持一个源文件,内容如下:
obj-m := linux-module-module.o MY_CFLAGS += -g -DDEBUG ccflags-y += ${MY_CFLAGS} SRC := $(shell pwd) all: $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install: $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install clean: rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c rm -f Module.markers Module.symvers modules.order rm -rf .tmp_versions Modules.symvers
原始的Makefile,支持模块在内核源码目录外编译。编译的命令是“$(MAKE) -C $(KERNEL_SRC) M=$(SRC)”。其中-C选项将当前工作目录转移到指定的位置;KERNEL_SRC是Yocto/PetaLinux在文件components/yocto/layers/core/meta/classes/module.bbclass里定义的变量,指定了Linux内核源代码目录,一般是Petalinux工程目录下的子目录“build/tmp/work-shared/zynqmp-generic/kernel-source”。这样编译时先进入内核源代码目录,先执行其中的顶层Makefile。M是内核根目录下的Makefile中使用的变量,让make在构造modules目标之前返回到内核模块源的代码目录。
Linux内核顶层Makefile关于M变量的代码:
# Use make M=dir or set the environment variable KBUILD_EXTMOD to specify the # directory of external module to build. Setting M= takes precedence. ifeq ("$(origin M)", "command line") KBUILD_EXTMOD := $(M) endif
module.bbclass里定义的变量KERNEL_SRC的代码:
python do_devshell_prepend () { os.environ['CFLAGS'] = '' os.environ['CPPFLAGS'] = '' os.environ['CXXFLAGS'] = '' os.environ['LDFLAGS'] = '' os.environ['KERNEL_PATH'] = d.getVar('STAGING_KERNEL_DIR') os.environ['KERNEL_SRC'] = d.getVar('STAGING_KERNEL_DIR') os.environ['KERNEL_VERSION'] = d.getVar('KERNEL_VERSION') os.environ['CC'] = d.getVar('KERNEL_CC') os.environ['LD'] = d.getVar('KERNEL_LD') os.environ['AR'] = d.getVar('KERNEL_AR') os.environ['O'] = d.getVar('STAGING_KERNEL_BUILDDIR') kbuild_extra_symbols = d.getVar('KBUILD_EXTRA_SYMBOLS') if kbuild_extra_symbols: os.environ['KBUILD_EXTRA_SYMBOLS'] = kbuild_extra_symbols else: os.environ['KBUILD_EXTRA_SYMBOLS'] = '' }
语句“obj-m := linux-test-module.o”指示Kbuild将模块编译成Linux内核模块文件linux-test-module.ko。
更多信息可以参考相关文档,比如深入理解 Linux 配置/构建系统是如何工作的,LINUX内核编译学习笔记, Linux 内核模块编译 Makefile, Linux 2.6内核Makefile浅析。
多源文件Makefile
如果有多个源文件,也可以学习Linux内核模块的Makefile写法,使Petalinux的内核模块也支持多个源文件。Linux内核通过$(
export TEST_MODULE_NAME = linux-test-module export TEST_FILES = linux-test-module.c linux-test-module-internal.c $(TEST_MODULE_NAME)-objs = $(patsubst %.c,%.o,$(filter %.c,$(TEST_FILES))) obj-m += $(TEST_MODULE_NAME).o MY_CFLAGS += -g -DDEBUG ccflags-y += ${MY_CFLAGS} SRC := $(shell pwd) all: $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install: $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install clean: rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c rm -f Module.markers Module.symvers modules.order rm -rf .tmp_versions Modules.symvers
内核源码目录内Makefile
有时内核模块也需要在Linux内核源码目录内编译,不需要指定编译命令。KERNELRELEASE是Linux内核源码的顶层Makefile定义的一个变量。如果在内核源码目录内编译,先执行Linux内核源码的顶层Makefile,会定义KERNELRELEASE的具体值。如果加上对KERNELRELEASE的检查,前面的Makefile可以改造成既可以在在内核源码目录内编译,也可以在在内核源码目录外编译。下面的Makefile,增加了检查KERNELRELEASE的语句“ifeq ($(KERNELRELEASE),)”。
export TEST_MODULE_NAME = linux-test-module export TEST_FILES = linux-test-module.c linux-test-module-internal.c $(TEST_MODULE_NAME)-objs = $(patsubst %.c,%.o,$(filter %.c,$(TEST_FILES))) obj-m += $(TEST_MODULE_NAME).o MY_CFLAGS += -g -DDEBUG ccflags-y += ${MY_CFLAGS} ifeq ($(KERNELRELEASE),) SRC := $(shell pwd) # KERNEL_src=/proj/hankf/zcu106/rdf0428-zcu106-vcu-trd-2020.2/source/linux-kernel all: $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install: $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install clean: rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c rm -f Module.markers Module.symvers modules.order rm -rf .tmp_versions Modules.symvers endif
实际测试中,不加KERNELRELEASE相关语句,也不影响在Linux内核源码目录内编译。据说旧版本Kbuild才需要的KERNELRELEASE相关语句。
上面的Makefile文件也可以作为一个模板。只需要更改TEST_MODULE_NAME的值“linux-test-module”,和更改TEST_FILES后的文件列表,可以用于编译其它模块。
如果既不在PetaLinux环境里编译,也不再Linux内核源码目录内编译,请再在上面的Makefile文件里定义内核源码目录。
Yocto recipe文件
Yocto recipe文件的扩展名是.bb,它定义recipe需要的文件。
原始bb文件
原始的recipe的bb文件的只含有一个C文件。
SUMMARY = "Recipe for build an external linux-test-module Linux kernel module" SECTION = "PETALINUX/modules" LICENSE = "GPLv2" LIC_FILES_CHKSUM = "file://COPYING;md5=12f884d2ae1ff87c09e5b7ccc2c4ca7e" inherit module INHIBIT_PACKAGE_STRIP = "1" SRC_URI = "file://Makefile \ file://linux-test-module.c \ file://COPYING \ " S = "${WORKDIR}" # The inherit of module.bbclass will automatically name module packages with # "kernel-module-" prefix as required by the oe-core build environment.
新bb文件
由于要支持多个源文件,需要在recipe的bb文件的添加所有C文件。修改其实也很简单。下面的bb文件添加了行“file://linux-test-module-internal.c”以为Yocto/PetaLinux增加文件linux-test-module-internal.c。
SUMMARY = "Recipe for build an external linux-test-module Linux kernel module" SECTION = "PETALINUX/modules" LICENSE = "GPLv2" LIC_FILES_CHKSUM = "file://COPYING;md5=12f884d2ae1ff87c09e5b7ccc2c4ca7e" inherit module INHIBIT_PACKAGE_STRIP = "1" SRC_URI = "file://Makefile \ file://linux-test-module.c \ file://linux-test-module-internal.c \ file://COPYING \ " S = "${WORKDIR}" # The inherit of module.bbclass will automatically name module packages with # "kernel-module-" prefix as required by the oe-core build environment.
测试环境
UBuntu 18.04
PetaLinux 2020.2