1、简介
《沉浸式剖析OpenHarmony源代码》一书没有涉及系统移植方面的内容,因此在交了书稿到出版社之后,我就开始考虑系统移植方面的事情。在了解一些情况后综合考量,我决定尝试一下在Raspberry Pi 4B上移植OHOS,因为已经有成功的先例了,一来可行性没问题,二来可以少走很多弯路。
本次移植参考了社区大佬 亮子力的“harmony-raspberry: 移植鸿蒙Harmony到树莓派 (gitee.com)” 和 hazhuzhu哈猪猪 的“#星光计划2.0# OpenHarmony3.0的树莓派4B移植-学习记录-开源基础软件社区-51CTO.COM” 。二位的成功移植案例,给了我不少启发,虽然在实际移植过程中,我也经历了一些他们没遇到过的问题,但最终都比较顺利地解决掉了。
下面是我将OHOS LTS3.0系统移植到 Raspberry Pi 4B 开发板上所做的一些事情,感兴趣的小伙伴可以参考下面的步骤进行操作,应该也能比较顺利地完成初步的移植工作。
视频链接:https://ost.51cto.com/show/13949
本次移植的相关代码见码云仓库:
https://gitee.com/liangkzgitee/ohoslts30rpi4b
已确认可运行的烧录镜像见百度网盘:
注意:本例触屏方案为“EDT_FT5X06”,其他方案的触屏可能会有显示但无触碰功能。
链接:https://pan.baidu.com/s/1KvSGFBf6pdyqdJTTq_1O6A?pwd=ohos
提取码:ohos
链接内OhosLts30Pi4b目录下的文件列表:
1.ohos_rpi4b.img:完整的系统镜像,直接用Raspberry Pi官方烧录工具烧录到SD卡上即可使用。
2.zImage:内核镜像,可用于直接替换SD卡上“/boot/”分区里的内核文件。
3.boot.img:烧录到SD卡上“/boot/”分区里的内容,已包含内核镜像。
4.system.img:OHOS系统镜像,Linux下可用dd命令单独烧录到SD卡内的“/”分区,用sudo权限可修改该分区内的文件,比如/system/etc/init.rpi4b.cfg 或 /system/etc/weston.ini 等。
5.vendor.img 和 userdata.img:目前并无实质性的重要内容,正常烧录一次后就不用再管这两个镜像了。
2、OHOS LTS3.0
创建 LTS30 目录,在该目录内下载 OHOS LTS3.0 源代码:
$ repo init -u git@gitee.com:openharmony/manifest.git -b refs/tags/OpenHarmony-v3.0-LTS --no-repo-verify
$ repo sync -c -j4
$ repo forall -c 'git lfs pull'
$ ./build/prebuilts_download.sh
3、产品配置部分
(1)rpi4b.json 设备部分
//productdefine/common/device/目录下,拷贝“hi3516dv300.json”,并重命名为“rpi4b.json”,并修改相关字段:
{
"device_name": "rpi4b",
"device_company": "raspberrypi",
"target_os": "ohos",
"target_cpu": "arm",
"kernel_version": "",
"device_build_path": "device/raspberrypi/build"
}
(2)rpi4b.json 产品部分
//productdefine/common/products/目录下,拷贝“Hi3516DV300.json”,并重命名为 “rpi4b.json”,并修改相关字段:
{
"product_name": "rpi4b",
"product_company": "raspberrypi",
"product_device": "rpi4b",
"version": "2.0",
"type": "standard",
"product_build_path": "device/raspberrypi/build",
"parts":{
"ace:ace_engine_standard":{},
"ace:napi":{},
......
#【删除“hisilicon_products”这一行】
"hisilicon_products:hisilicon_products":{},
#【新增子系统和部件,//build/subsystem_config.json文件中不添加该子系统信息也没关系】
"raspberrypi_products:raspberrypi_products":{},
......#【略,其他子系统/部件列表保持与Hi3516DV300一致即可,也可自行裁剪】
"ark:ark":{},
"ark:ark_js_runtime":{},
"ark:ark_ts2abc":{}
}
}
可以对部件列表进行一下裁剪,把不相关部分部件去掉,但要注意可能会因为依赖关系的原因导致编译异常。
4、vendor部分
创建//vendor/raspberrypi/rpi4b/目录,整体拷贝//vendor/hisilicon/Hi3516DV300/目录下的 hdf_config 目录到 //vendor/raspberrypi/rpi4b/ 目录下,修改 hdf_config/khdf/hdf.hcs 文件为:
#include "device_info/device_info.hcs"
root {
module = "raspberry,bcm2711_chip";
}
本例是有移植HDF的,不过在8.3的内核配置中,把“CONFIG_DRIVERS_HDF_TEST”注释掉了,因此这里需要增加一个Makefile去直接编译 hdf_config/khdf/hdf.hcs, 而不去编译 hdf_config/khdf/hdf_test/hdf.hcs。
以后再根据Raspberry Pi 4B的硬件实情来修改hdf_config的其他部分。
5、device部分
增加//device/raspberrypi/目录下的内容,见本仓库//device/raspberrypi/目录下的文件修改和同目录下的README.md文档
6、init_lite部分
修改//base/startup/init_lite/services/BUILD.gn 文件,增加下面一句:
# init etc files group
ohos_prebuilt_etc("init.cfg") {
source = "//base/startup/init_lite/services/etc/init.cfg"
#add for rpi4b begin:
if( product_name == "rpi4b" ) {
source = "//base/startup/init_lite/services/etc/rpi4b_init_cfg/init.cfg"
}
#add for rpi4b end.
part_name = "init"
}
//base/startup/init_lite/services/etc/目录下增加 “rpi4b_init_cfg/”目录,将同目录系的init.cfg文件拷贝进去,并做修改。
7、drivers部分
把//drivers/peripheral/camera/hal/adapter/chipset/gni/ 目录下的camera.rpi3.gni拷贝一份,并重命名为camera.rpi4b.gni,内容不需要修改。
8、kernel部分
(1)//kernel/linux/ 内核代码部分
Linux内核,不用OHOS自带的 linux-5.10 源代码,而是用RaspberryPi官方的5.10版本源代码,这样可以免去内核部分的移植工作。
在 //kernel/linux/ 目录下创建 “linux-5.10-rpi4b” 目录,把RaspberryPi官方的5.10分支内核源代码,下载到该目录下:
git clone https://github.com/raspberrypi/linux linux-5.10-rpi4b
如果无法通过github下载,那就在gitee上搜索同版本的镜像代码下载回来使用也可以。
(2)//kernel/linux/build/ 编译脚本部分
OHOS内核默认编译生成 uImage,rpi4b 需要修改编译生成 zImage。
BUILD.gn 文件内:
增加下面两处对(device_name == “rpi4b”)的判断和修改,可以让rpi4b产品选用 8.1 中下载的“linux-5.10-rpi4b”内核源代码去生成zImage镜像文件。
if (device_name == "rpi4b") {
kernel_source_dir = "//kernel/linux/$linux_kernel_version-$device_name"
}
......
if (device_name == "rpi4b") {
outputs = [ "$root_build_dir/packages/phone/images/zImage" ]
} else {
outputs = [ "$root_build_dir/packages/phone/images/uImage" ]
}
build_kernel.sh 文件内:
if [ "$7" == "rpi4b" ];then
# 将zImage、dtb、overlays、modules拷贝到images目录下备用
cp ${2}/kernel/src_tmp/${8}/arch/arm/boot/zImage ${3}/zImage
cp ${2}/kernel/src_tmp/${8}/arch/arm/boot/dts/bcm2711-rpi-4-b.dtb ${3}/
cp -r ${2}/kernel/src_tmp/${8}/arch/arm/boot/dts/overlays ${3}/
cp -r ${2}/kernel/src_tmp/${8}/modules ${3}/
# 删除modules下的两个软链接,否则在制作img阶段会把软链接的内容一并拷贝,会出现异常。
rm ${3}/modules/lib/modules/5.10*/build
rm ${3}/modules/lib/modules/5.10*/source
else
#if [ "$7" == "hi3516dv300" ];then
cp ${2}/kernel/src_tmp/${8}/arch/arm/boot/uImage ${3}/uImage
fi
#在上述操作中拷贝的modules目录,将会被拷贝到//out/ohos-arm-release/packages/phone/system/lib/目录下,一并被打包到system.img内,烧录到SD卡里,系统启动时会insmod其中的部分模块。
kernel_module_build.sh 文件内:
if [ "$5" == "rpi4b" ];then
LINUX_KERNEL_UIMAGE_FILE=${LINUX_KERNEL_OUT}/arch/arm/boot/zImage
else
LINUX_KERNEL_UIMAGE_FILE=${LINUX_KERNEL_OUT}/arch/arm/boot/uImage
fi
kernel.mk 文件内:
从上到下的修改(请搜索关键字“rpi4b”):
- 增加编译modules的安装路径:
KERNEL_SRC_TMP_PATH := $(OUT_DIR)/kernel/src_tmp/${KERNEL_VERSION}
ifeq ($(DEVICE_NAME), rpi4b)
KERNEL_MODULES_PATH := $(OUT_DIR)/kernel/src_tmp/${KERNEL_VERSION}/modules
endif
- 修改内核源代码路径:
ifeq ($(DEVICE_NAME), rpi4b)
KERNEL_SRC_PATH := $(OHOS_BUILD_HOME)/kernel/linux/${KERNEL_VERSION}-${DEVICE_NAME}
else
KERNEL_SRC_PATH := $(OHOS_BUILD_HOME)/kernel/linux/${KERNEL_VERSION}
endif
- 修改编译内核的工具链:
OHOS默认使用 arm-linux-gnueabi 编译工具来编译Linux内核,用这个编译rpi4b使用的内核也可以。
但建议用RaspberryPi官方推荐的 arm-linux-gnueabihf 编译工具来编译内核(32位系统)。
请先执行 “sudo apt install crossbuild-essential-armhf ” 将编译工具安装到默认的 /usr/bin/ 目录下,然后修改编译工具链:
ifeq ($(DEVICE_NAME), rpi4b)
KERNEL_TARGET_TOOLCHAIN := /usr/bin
KERNEL_TARGET_TOOLCHAIN_PREFIX := $(KERNEL_TARGET_TOOLCHAIN)/arm-linux-gnueabihf-
else
KERNEL_TARGET_TOOLCHAIN := $(PREBUILTS_GCC_DIR)/linux-x86/arm/gcc-linaro-7.5.0-arm-linux-gnueabi/bin
KERNEL_TARGET_TOOLCHAIN_PREFIX := $(KERNEL_TARGET_TOOLCHAIN)/arm-linux-gnueabi-
endif
- 修改编译目标:
ifeq ($(DEVICE_NAME), rpi4b)
KERNEL_IMAGE_FILE := $(KERNEL_SRC_TMP_PATH)/arch/arm/boot/zImage
else
KERNEL_IMAGE_FILE := $(KERNEL_SRC_TMP_PATH)/arch/arm/boot/uImage
endif
- 修改make命令的编译目标、安装modules到INSTALL_MOD_PATH指定的位置:
ifeq ($(DEVICE_NAME), rpi4b)
$(hide) $(KERNEL_MAKE) -C $(KERNEL_SRC_TMP_PATH) ARCH=$(KERNEL_ARCH) $(KERNEL_CROSS_COMPILE) -j64 zImage modules dtbs
$(hide) $(KERNEL_MAKE) -C $(KERNEL_SRC_TMP_PATH) ARCH=$(KERNEL_ARCH) $(KERNEL_CROSS_COMPILE) -j64 INSTALL_MOD_PATH=$(KERNEL_MODULES_PATH) modules_install
else
$(hide) $(KERNEL_MAKE) -C $(KERNEL_SRC_TMP_PATH) ARCH=$(KERNEL_ARCH) $(KERNEL_CROSS_COMPILE) -j64 uImage
endif
(3)//kernel/linux/config/ 编译配置部分
//kernel/linux/linux-5.10-rpi4b/arch/arm/configs/bcm2711_defconfig
将上述文件拷贝并改名到:
//kernel/linux/config/linux-5.10/arch/arm/configs/rpi4b_standard_defconfig
在该文件内增加Android Binder、OHOS HDF、Security部分的配置,详细配置的开关,见本仓库上述路径下的文件,这些配置可根据实际情况微调。
另外,为避免OHOS HDF的USB部分编译失败,需将 rpi4b_standard_defconfig 文件内的:
“CONFIG_USB_CONFIGFS=m”
修改为:
“CONFIG_USB_CONFIGFS=y”
将该模块直接编译进内核。
其他默认编译成模块的部分如:
CONFIG_DRM=m
...
CONFIG_DRM_V3D=m
CONFIG_DRM_VC4=m
等,可先保持不动,我们会在”5.device部分“的“init.rpi4b.cfg”文件中加载相应的模块起来运行即可。
或者在这里将相应的CONFIG_XXX配置成“=y”,直接将其编译进内核,则在”5.device部分“ 的 “init.rpi4b.cfg” 文件中就不需要 insmod 了。
(4)//kernel/linux/patches/ 编译补丁部分
//kernel/linux/patches/linux-5.10/rpi4b_patch/目录下两个patch文件。
hdf.patch 文件拷贝自 …/hi3516dv300_patch/hdf.patch,完全不用修改。
rpi4b.patch 文件原本应该是OHOS移植到rpi4b平台上所需要的大量适配代码的patch,但因为目前使用的kernel 代码是RaspberryPi官方的内核,已经包含所有的适配代码了,因此rpi4b.patch可以留空,这里是直接拷贝 hi3516dv300_small.patch,并做简单处理:
diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h
index ec84ad106568..9d7b05055726 100644
--- a/include/uapi/linux/android/binder.h
+++ b/include/uapi/linux/android/binder.h
@@ -27,6 +27,7 @@
#define B_PACK_CHARS(c1, c2, c3, c4) \
((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4))
#define B_TYPE_LARGE 0x85
+
enum {
BINDER_TYPE_BINDER = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),
9、third_party部分
(1)eudev
//third_party/eudev/rules.d/touchscreen.rules 文件的修改。
Raspberry Pi 4B的触碰显示屏,有多个硬件方案,不同方案需要加载不同的驱动程序。
对于我的Raspberry Pi 4B开发板,在Raspberry Pi 4B上运行官方系统时,通过查看触屏的设备信息和驱动程序(驱动程序用于insmod,或者在编译内核时直接编译进去),看到有"vc4"和"generic ft5x06 (79)"两个,把它们都加进rules文件中去。这一步需要请小伙伴们根据自己的触屏设备的实际信息修改和添加:
ATTRS{name}=="vc4", ENV{ID_INPUT}="1", ENV{ID_INPUT_TOUCHSCREEN}="1"
ATTRS{name}=="generic ft5x06 (79)", ENV{ID_INPUT}="1", ENV{ID_INPUT_TOUCHSCREEN}="1"
......
(2)weston.ini
//third_party/weston/weston.ini 文件的修改。
在我的环境下,不改这个文件,显示屏也会有正常显示,但是触屏的触碰输入功能会失效,必须要在此文件末尾增加如下修改,触碰输入功能才会正常。
[output]
name=card0
#name=HDMI-A-1
注意:如果Raspberry Pi 4B不带(或不接)触屏,则要把HDMI设备接到HDMI0端口上,再把上面的name字段设置为“HDMI-A-1”即可。如果HDMI设备接到HDMI1端口上,则只会在开机阶段显示终端上的log,weston运行起来之后会无输出了。
10、build 部分
(1)打包modules到system.img
修改 //build/ohos/images/build_image.py 脚本:
def _prepare_root(system_path):
......
os.symlink('/system/bin', os.path.join(root_dir, 'bin'))
os.symlink('/system/bin/init', os.path.join(root_dir, 'init'))
os.symlink('/system/etc', os.path.join(root_dir, 'etc'))
#rpi4b added begin:
# if modules_src exists:
# copy .../phone/images/modules/lib/modules to .../system/lib/modules
modules_src = os.path.join(system_path, "../images/modules/lib/modules")
if os.path.exists(modules_src):
modules_dest = os.path.join(system_path, 'lib/modules')
shutil.rmtree(modules_dest, ignore_errors=True)
shutil.copytree(modules_src, modules_dest, symlinks=True)
os.symlink('/system/lib', os.path.join(root_dir, 'lib'))
#rpi4b added end.
在 _prepare_root 函数末尾增加这几句话,把modules部分拷贝到system对应目录下,一并生成到system.img 镜像内,并生成 /lib 到 /system/lib 的软链接。【Hi3516DV300项目会因为不存在modules_src目录而不会跑拷贝的步骤】。
修改 //build/ohos/images/mkimage/dac.txt 文件,增加两句:
# dir
system/lib/modules, 00751, 0, 2000, 0
# file
system/lib/modules/*, 00755, 0, 2000, 0
为了让system.img、vendor.img、userdata.img镜像不至于太大,修改 //build/ohos/images/mkimage/ 目录下的:
system_image_conf.txt 文件【本文件实际可不改】:
-1610612224
+536869888
vendor_image_conf.txt 文件大小不改。
userdata_image_conf.txt 文件【本文件实际可不改】:
-1468006400
+268434944
(2)编译rpi4b产品
代码根目录下执行:
./build.sh --product-name rpi4b --ccache
即可开始编译rpi4b产品。
11、生成分离的烧录镜像
编译成功,生成的镜像有:zImage、system.img、vendor.img、userdata.img 这四个用于本项目的烧录,updater.img 镜像可先不管。
根据8.2的编译内核的脚本的更改,zImage、dtb、overlays、modules会被拷贝到images目录下。
根据10.1的打包镜像脚本的更改,modules会被拷贝到…/system/lib/modules/目录下,一并打包进system.img 里。
如果你已有烧录了Raspberry Pi官方系统的SD卡,可以保持上面的/boot分区不动,把这里的zImage拷贝进去,在config.txt文件中修改启动的内核为:kernel=zImage。
再在Linux下用fdisk命令,删除/rootfs分区,并重新建立三个主分区,分区大小建议大于 10.1 中修改的各分区镜像大小即可。再用dd命令分别将 system.img、vendor.img、userdata.img 三个镜像烧录到SD卡的mmcblk0p2、mmcblk0p3、mmcblk0p4 分区即可。
注意分区的烧录位置,system.img一定要烧录到mmcblk0p2分区,因为在/boot分区的cmdline.txt中指定了root分区的位置为 /dev/mmcblk0p2。
vendor.img和userdata.img两个镜像分别烧录到mmcblk0p3、mmcblk0p4分区。在 /system/etc/init.cfg 中有:
"mount ext4 /dev/block/mmcblk0p3 /vendor wait rdonly barrier=1",
"mount ext4 /dev/block/mmcblk0p4 /data wait nosuid nodev noatime barrier=1,data=ordered,noauto_da_alloc"
会把它们分别挂载到/vendor和/data下。
12、生成合并的ohos_rpi4b.img烧录镜像
如果想要生成合并的系统镜像,可以参考并使用亮子力的脚本,见仓库:harmony-raspberry: 移植鸿蒙Harmony到树莓派 (gitee.com) 内的使用说明。
本地步骤如下:
(1)整体拷贝烧录官方rpi4b系统的SD卡的boot分区内容到://device/raspberrypi/rpi4b/build/boot/ 目录下。
(2)根据实际情况修改 cmdline.txt 中的root字段:
... root=/dev/mmcblk0p2 ...
(3)修改 config.txt 文件,将启动的内核镜像修改为 zImage:
# which kernel to boot
kernel=zImage
# 启用fake KMS,而不是原来的vc4-kms-v3d
dtoverlay=vc4-fkms-v3d
(4)创建 //device/raspberrypi/mkimg/目录,把亮子力的仓库中的:
https://gitee.com/liangzili/harmony-raspberry/blob/master/rpi4b/device/raspberrypi/images/
目录下的脚本拷贝到此目录下(实际只需要mkboot.py 和 mergeImg.sh)。
在项目代码根目录下执行mkboot.py脚本命令,以生成 boot.img 镜像:
python device/raspberrypi/mkimg/mkboot.py --input-path ./out/ohos-arm-release/packages/phone --output-image ./out/ohos-arm-release/packages/phone/images/boot.img
在项目代码根目录下执行mergeImg.sh脚本命令,以生成 ohos_rpi4b.img 镜像:
./device/raspberrypi/mkimg/mergeImg.sh ./out/ohos-arm-release/packages/phone/images/boot.img ./out/ohos-arm-release/packages/phone/images/system.img ./out/ohos-arm-release/packages/phone/images/vendor.img ./out/ohos-arm-release/packages/phone/images/userdata.img ./out/ohos-arm-release/packages/phone/images/ohos_rpi4b.img
将 ohos_rpi4b.img 拷贝到Windows系统硬盘下,通过RaspberryPi官方的Imager烧录工具将其烧录到SD卡即可。
以后再修改代码和编译OHOS后:
- 如只涉及内核的修改(不涉及modules的动态加载),可直接将新生成的zImage文件拷贝并替换SD卡内的boot分区中的zImage文件(或相关文件)即可。
- 如只涉及system分区内容的相关修改(包括涉及modules的动态加载),可直接在Linux命令行下,使用 “dd” 命令单独烧录system.img镜像到SD卡的 /dev/mmcblk0p2 分区即可,不需要重新打包成ohos_rpi4b.img 和重新烧录全部分区了。
- 默认的vendor分区和userdata分区,目前没有什么重要内容需要在rpi4b开发板上用到,但这两个分区会默认挂载到根目录下,暂没必要重复烧录。