::: hljs-center

OHOS LTS 3.0移植到RaspberryPi 4B

liangkz

:::

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. 产品配置部分

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"
}

3.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部分

8.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上搜索同版本的镜像代码下载回来使用也可以。

8.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”):

  1. 增加编译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
  1. 修改内核源代码路径:
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
  1. 修改编译内核的工具链

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
  1. 修改编译目标:
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
  1. 修改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

8.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 了。

8.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部分

9.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"
......

9.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 部分

10.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

10.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) 内的使用说明。

本地步骤如下:

12.1 整体拷贝烧录官方rpi4b系统的SD卡的boot分区内容到://device/raspberrypi/rpi4b/build/boot/ 目录下。

12.2 根据实际情况修改 cmdline.txt 中的root字段:

... root=/dev/mmcblk0p2 ...

12.3 修改 config.txt 文件,将启动的内核镜像修改为 zImage:

# which kernel to boot
kernel=zImage

# 启用fake KMS,而不是原来的vc4-kms-v3d
dtoverlay=vc4-fkms-v3d

12.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开发板上用到,但这两个分区会默认挂载到根目录下,暂没必要重复烧录。

附件链接:https://ost.51cto.com/resource/2085

想了解更多关于开源的内容,请访问:

51CTO 开源基础软件社区

https://ost.51cto.com/#bkwz