OpenHarmony 2.0 Canary Linux Kernel 的编译流程 原创 精华
OpenHarmony 2.0 Canary Linux Kernel的编译流程
liangkz 2021.09.09
OpenHarmony 2.0 Canary编译标准系统,默认使用的是Linux 4.19版本内核,在Canary上编译使用Linux内核的小型系统,cute(*^ω^*)遇见 已经给出过一篇详细的总结《编译linux内核的openharmony》,不过是在docker环境下编译的,在Linux环境下编译ipcamera_hispark_taurus_linux的话,需要先安装gcc-arm-linux-gnueabi编译工具:
$sudo apt install gcc-arm-linux-gnueabi
$arm-linux-gnueabi-gcc -v
然后就可以hb set 进行项目选择和 hb build 进行编译了。编译输出四个镜像:uImage_hi3516dv300_smp(内核镜像uImage改名而来)、rootfs_ext4.img、userfs_ext4.img、userdata_ext4.img,其中userdata_ext4.img烧录不烧录都没问题,注意fastboot引导命令的参数即可,与使用LiteOS_A内核的稍有不同:
setenv bootargs "mem=128M console=ttyAMA0,115200 root=/dev/mmcblk0p3 rw rootfstype=ext4 rootwait selinux=0 rootdelay=5 blkdevparts=mmcblk0:1M(boot),9M(kernel),50M(rootfs),50M(userfs),-(userdata)"
setenv bootcmd "mmc read 0x0 0x82000000 0x800 0x4800;bootm 0x82000000"
saveenv
reset
这里是烧录了userdata_ext4.img后,增加了,-(userdata)参数,不烧录就去掉。
标准系统的Linux内核和小型系统的Linux内核,是共用的,当然也可以配置为一个使用linux-5.10,另一个使用linux-4.19,针对性地修改一下编译配置脚本即可。
下面我们来详细看一下linux内核的编译流程和如何配置编译linux-5.10内核。
标准系统单独编译Linux内核组件的指令为:
$./build.sh --product-name Hi3516DV300 --ccache --build-target linux_kernel
而小型系统单独编译Linux内核组件的指令为:
$hb build -T linux_kernel
当执行上述语句,或者全编译系统编译到内核组件时,编译到内核组件的入口在:
//kernel/linux/build/BUILD.gn
打开这个文件看一下:
declare_args() {
#默认Linux内核版本是4.19,想要编译5.10版本的内核,修改办法见下面【7-1】的说明
kernel_version = "linux-4.19"
}
if (defined(ohos_lite)) {
#小型系统的Linux内核编译入口
build_ext_component("linux_kernel") {
exec_path = rebase_path(".", root_build_dir)
outdir = rebase_path("$root_out_dir")
clang_dir = ""
if (ohos_build_compiler_dir != "") {
clang_dir = rebase_path("${ohos_build_compiler_dir}")
}
build_type = "small"
product_path_rebase = rebase_path(product_path, ohos_root_path)
#小型系统编译Linux内核的执行脚本
command = "./kernel_module_build.sh ${outdir} ${build_type} ${clang_dir} \
${product_path_rebase} ${board_name} ${kernel_version}"
deps = [ "//build/lite:mark_as_prebuilts" ]
}
} else {
#标准系统的Linux内核编译入口
kernel_build_script_dir = "//kernel/linux/build"
#内核源代码是//kernel/linux/linux-4.19/,而不是//kernel/linux-4.19/,这个要注意
kernel_source_dir = "//kernel/linux/$kernel_version"
action("build_kernel") {
#标准系统编译Linux内核的执行脚本,会调用kernel_module_build.sh
script = "build_kernel.sh"
sources = [ kernel_source_dir ]
product_path="vendor/$product_company/$product_name"
build_type = "standard"
outputs = [ "$root_build_dir/packages/phone/images/uImage" ]
args = [
rebase_path(kernel_build_script_dir, root_build_dir),
rebase_path("$root_out_dir/../KERNEL_OBJ"),
rebase_path("$root_build_dir/packages/phone/images"),
build_type,
rebase_path("$clang_base_path"),
product_path,
device_name,
kernel_version
]
}
group("linux_kernel") {
deps = [
":build_kernel",
]
}
}
小型系统与标准系统编译Linux内核的脚本可以说是一样的,只是两者编译的参数稍有不同,导致Linux内核源代码拷贝到out目录下的路径有所不同而已,其它流程都是一样的。
先看标准系统编译内核的build_kernel.sh脚本:
./kernel_module_build.sh ${2} ${4} ${5} ${6} ${7} ${8}
mkdir -p ${3}
cp ${2}/kernel/src_tmp/${8}/arch/arm/boot/uImage ${3}/uImage
就是去掉1/3两个参数再调用kernel_module_build.sh,生成内核镜像uImage,然后再将uImage拷贝到参数3指定的路径下即可。
再看kernel_module_build.sh脚本,前面的if...elif判断,主要是为了配置LINUX_KERNEL_OUT路径和编译工具链等参数,标准系统和小型系统的配置有细微的差别。
接下来是“make -f kernel.mk”,编译成功或失败,都会echo相应的提示到终端。
编译成功的话,小型系统则在这里执行拷贝操作,uImage镜像名字改成了uImage_hi3516dv300_smp:
cp -rf ${LINUX_KERNEL_UIMAGE_FILE} ${OUT_DIR}/uImage_${DEVICE_NAME}_smp
编译成功的话,标准系统则在上面的build_kernel.sh脚本中执行拷贝操作。
打开kernel.mk查看,编译内核也就这几步:
$(KERNEL_IMAGE_FILE):
$(hide) echo "build kernel..."
【1】 $(hide) rm -rf $(KERNEL_SRC_TMP_PATH);mkdir -p $(KERNEL_SRC_TMP_PATH);cp -arfL $(KERNEL_SRC_PATH)/* $(KERNEL_SRC_TMP_PATH)/
【2】 $(hide) cd $(KERNEL_SRC_TMP_PATH) && patch -p1 < $(HDF_PATCH_FILE) && patch -p1 < $(DEVICE_PATCH_FILE)
【3】 $(hide) cp -rf $(KERNEL_CONFIG_PATH)/. $(KERNEL_SRC_TMP_PATH)/
【4】 $(hide) $(KERNEL_MAKE) -C $(KERNEL_SRC_TMP_PATH) ARCH=$(KERNEL_ARCH) $(KERNEL_CROSS_COMPILE) distclean
【5】 $(hide) $(KERNEL_MAKE) -C $(KERNEL_SRC_TMP_PATH) ARCH=$(KERNEL_ARCH) $(KERNEL_CROSS_COMPILE) $(DEFCONFIG_FILE)
ifeq ($(KERNEL_VERSION), linux-5.10)
【6】 $(hide) $(KERNEL_MAKE) -C $(KERNEL_SRC_TMP_PATH) ARCH=$(KERNEL_ARCH) $(KERNEL_CROSS_COMPILE) modules_prepare
endif
【7】 $(hide) $(KERNEL_MAKE) -C $(KERNEL_SRC_TMP_PATH) ARCH=$(KERNEL_ARCH) $(KERNEL_CROSS_COMPILE) -j64 uImage
endif
.PHONY: build-kernel
build-kernel: $(KERNEL_IMAGE_FILE)
下面就在我的编译环境下简单看一下做了些什么:
【7-1】删除KERNEL_SRC_TMP_PATH目录下的Linux Kernel源代码拷贝(包括上一次编译生成的中间文件和镜像文件),重新生成一个空的目录,再将KERNEL_SRC_PATH目录整体拷贝过去。
KERNEL_SRC_PATH目前配置是//kernel/linux/linux-4.19/,而不是//kernel/linux-4.19/,估计是与build/config/patches目录的位置有关。
【2021.08.31~09.07之间的代码,开始支持编译Linux5.10内核】如果要编译5.10版本的内核,需要改以下三处地方:
【A】//kernel/linux/build/BUILD.gn 的 kernel_version 字段修改成linux-5.10,
【B】//device/hisilicon/hispark_taurus/sdk_linux/config.gni 的 kernel_version 字段修改成linux-5.10,
【C】//device/hisilicon/hispark_taurus/sdk_linux/build.sh 的 export KERNEL_VERSION 字段修改成linux-5.10
再去编译时,KERNEL_SRC_PATH就会配置成//kernel/linux/linux-5.10/,去编译5.10版本内核了,编译出来的镜像烧录到开发板上也能正常跑起来,log如下:
## Booting kernel from Legacy Image at 82000000 ...
Image Name: Linux-5.10.57
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 4987452 Bytes = 4.8 MiB
Load Address: 80008000
Entry Point: 80008000
Loading Kernel Image
Starting kernel ...
Booting Linux on physical CPU 0x0
Linux version 5.10.57 (ohos@ubuntu) (OHOS () clang version 10.0.1.69957 ......
2021.09.07之后的代码,对编译Linux内核的相关脚本做了调整【见评论】,默认编译Linux5.10版本内核,如需要编译Linux 4.19版本内核,只需要修改//build/ohos/kernel/kernel.gni 文件下的 linux_kernel_version = "linux-5.10" 为4.19版本即可,其它文件的内核版本都是从这里引用的。
默认编译"linux-5.10"时,虽然 //device/hisilicon/hispark_taurus/sdk_linux/config.gni 中也有一个 kernel_version = "4.19",但这个kernel_version看上去并没有使用到,不用管。
另外,09.07之后的代码,也删除了 //kernel/linux-4.19/ 目录,避免了代码引用的困扰。
KERNEL_SRC_TMP_PATH目录,按编译4.19版本内核来看:
标准系统:out/KERNEL_OBJ/kernel/src_tmp/linux-4.19/
小型系统:out/hispark_taurus/ipcamera_hispark_taurus_linux/kernel/linux-4.19/
【7-2】进入KERNEL_SRC_TMP_PATH,把//kernel/linux/patches/linux-4.19/hi3516dv300_patch/目录下的hdf.patch和hi3516dv300.patch两个patch加入到Linux Kernel中,一起编译进内核镜像里。
hdf.patch会把//drivers/framework/目录和//drivers/adapter/khdf/linux/目录软链接到KERNEL_SRC_TMP_PATH/drivers/hdf/目录下,编译这两个目录的时候,会在 //drivers/framework/ 和 //drivers/adapter/khdf/linux/目录下生成中间文件,其它的相关的修改如Makefile等,会patch到相关文件中。
hi3516dv300.patch主要是针对hi3516dv300开发板的板级硬件的驱动适配,包括设备树文件、具体硬件模块配置、驱动代码、编译配置等等。
【7-3】Hi3516开发板的CPU是32位的arm架构芯片,这一步将//kernel/linux/config/linux-4.19/目录下的芯片架构相关配置文件拷贝到KERNEL_SRC_TMP_PATH对应的子目录下,编译时据此进行针对性地编译相关模块。
【7-4】到【7-7】就是具体的make指令编译过程了,不再赘述。
因为CPU是32位的arm架构芯片,编译生成的uImage镜像在KERNEL_SRC_TMP_PATH/arch/arm/boot/目录下,在前面的build_kernel.sh/kernel_module_build.sh中,会被拷贝或改名拷贝到对应的目录下,被烧录工具拿去烧录。
附件是我单独编译标准系统的Linux内核的部分log。
流程很清晰,学习一波,回去试试。
写上面这篇帖子所用的代码,是master分支,大概是8.30~9.5之间不记得是哪天更新的了,好好地编译小型系统+Linux4.19或5.10内核,都能好好工作,标准系统+Linux4.19内核也能好好工作,昨晚发现标准系统+Linux5.10内核会出现kernel panic现象,没找到原因。
今早起来,无意去 repo sync了一下代码,发现有不少改动,与上文提到的修改有了差异,赶紧到gitee看一下kernel_linux_build等相关仓库,~.~!!
9.7 对内核编译部分做了一些调整,上文提到的修改位置看起来已经不准确了。
学习学习~