编辑
2025-01-22
工作知识
0

为了使得openharmony能够通过qemu启动,我们需要在设备上具备一个qemu的环境,这里仅针对x86-64,arm-linux类似x86-64,但是这里没有验证,对于windows,这里直接下载安装即可,对于linux,这里推荐使用qemu来源码构建。本文指定qemu的版本在8.2.7(2024/09/18)和windows的8.2.0

一、windows下的qemu安装

1.1 下载qemu版本

为了下载qemu,我们需要Stefan Weil给我们预编译的windows二进制:

https://www.qemu.org/download/#windows

我们找2023-12-24为我们预编译的qemu版本8.2.0版本,如下链接

https://qemu.weilnetz.de/w64/2023/ https://qemu.weilnetz.de/w64/2023/qemu-w64-setup-20231224.exe

下载可获得二进制qemu-w64-setup-20231224.exe

1.2 安装

这里安装直接一路通过即可,我们需要注意的是,安装完需要设置windows的系统path变量,设置之后,我们如下能够看到命令即完成

qemu-system-aarch64.exe --version QEMU emulator version 8.0.90 (v8.1.0-rc0-12027-g6fd99fb6ae) Copyright (c) 2003-2023 Fabrice Bellard and the QEMU Project developers

二、linux下的qemu编译

在linux下使用qemu,我们更倾向于重新编译构建,这里介绍方法

2.1 下载qemu源码

我们可以wget获取,如下

wget https://download.qemu.org/qemu-8.2.7.tar.xz

下载后,解压即可

xz -d qemu-8.2.7.tar.xz && tar xvf qemu-8.2.7.tar

2.2 配置qemu

qemu源码内部可以直接configure,我们事先不清楚qemu功能是否齐全,我们可以先配置一下,如果没有包含自己想要的功能,那就需要安装对于的开发包,因为configure是根据系统环境来判断功能是否开启的,如下:

qemu 8.2.7 Build environment Build directory : /root/tf/qemu/qemu-8.2.7/build Source path : /root/tf/qemu/qemu-8.2.7 Download dependencies : YES Directories Build directory : /root/tf/qemu/qemu-8.2.7/build Source path : /root/tf/qemu/qemu-8.2.7 Download dependencies : YES Install prefix : /usr/local BIOS directory : share/qemu firmware path : share/qemu-firmware binary directory : /usr/local/bin library directory : /usr/local/lib/x86_64-linux-gnu module directory : lib/x86_64-linux-gnu/qemu libexec directory : /usr/local/libexec include directory : /usr/local/include config directory : /usr/local/etc local state directory : /var/local Manual directory : /usr/local/share/man Doc directory : /usr/local/share/doc Host binaries python : /root/tf/qemu/qemu-8.2.7/build/pyvenv/bin/python3 (version: 3.8) sphinx-build : NO gdb : /usr/bin/gdb iasl : NO genisoimage : /usr/bin/genisoimage smbd : /usr/sbin/smbd Configurable features Documentation : NO system-mode emulation : YES user-mode emulation : YES block layer : YES Install blobs : YES module support : NO fuzzing support : NO Audio drivers : pa oss Trace backends : log D-Bus display : YES QOM debugging : YES Relocatable install : YES vhost-kernel support : YES vhost-net support : YES vhost-user support : YES vhost-user-crypto support : YES vhost-user-blk server support : YES vhost-vdpa support : YES build guest agent : YES Compilation host CPU : x86_64 host endianness : little C compiler : cc -m64 -mcx16 Host C compiler : cc -m64 -mcx16 C++ compiler : NO Objective-C compiler : NO CFLAGS : -g -O2 QEMU_CFLAGS : -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fno-strict-aliasing -fno-common -fwrapv -fstack-protector-strong QEMU_LDFLAGS : -fstack-protector-strong -Wl,-z,relro -Wl,-z,now -Wl,--warn-common link-time optimization (LTO) : NO PIE : YES static build : NO malloc trim support : YES membarrier : NO debug graph lock : NO debug stack usage : NO mutex debugging : NO memory allocator : system avx2 optimization : YES avx512bw optimization : YES avx512f optimization : NO gcov : NO thread sanitizer : NO CFI support : NO strip binaries : NO sparse : NO mingw32 support : NO Cross compilers aarch64 : aarch64-linux-gnu-gcc x86_64 : cc i386 : cc Targets and accelerators KVM support : YES HVF support : NO WHPX support : NO NVMM support : NO Xen support : NO Xen emulation : YES TCG support : YES TCG backend : native (x86_64) TCG plugins : YES TCG debug enabled : NO target list : aarch64_be-linux-user aarch64-linux-user alpha-linux-user armeb-linux-user arm-linux-user cris-linux-user hexagon-linux-user hppa-linux-user i386-linux-user loongarch64-linux-user m68k-linux-user microblazeel-linux-user microblaze-linux-user mips64el-linux-user mips64-linux-user mipsel-linux-user mips-linux-user mipsn32el-linux-user mipsn32-linux-user nios2-linux-user or1k-linux-user ppc64le-linux-user ppc64-linux-user ppc-linux-user riscv32-linux-user riscv64-linux-user s390x-linux-user sh4eb-linux-user sh4-linux-user sparc32plus-linux-user sparc64-linux-user sparc-linux-user x86_64-linux-user xtensaeb-linux-user xtensa-linux-user aarch64-softmmu alpha-softmmu arm-softmmu avr-softmmu cris-softmmu hppa-softmmu i386-softmmu loongarch64-softmmu m68k-softmmu microblazeel-softmmu microblaze-softmmu mips64el-softmmu mips64-softmmu mipsel-softmmu mips-softmmu nios2-softmmu or1k-softmmu ppc64-softmmu ppc-softmmu riscv32-softmmu riscv64-softmmu rx-softmmu s390x-softmmu sh4eb-softmmu sh4-softmmu sparc64-softmmu sparc-softmmu tricore-softmmu x86_64-softmmu xtensaeb-softmmu xtensa-softmmu default devices : YES out of process emulation : YES vfio-user server : NO Block layer support coroutine backend : ucontext coroutine pool : YES Block whitelist (rw) : Block whitelist (ro) : Use block whitelist in tools : NO VirtFS (9P) support : YES VirtFS (9P) Proxy Helper support (deprecated): NO Live block migration : YES replication support : YES bochs support : YES cloop support : YES dmg support : YES qcow v1 support : YES vdi support : YES vhdx support : YES vmdk support : YES vpc support : YES vvfat support : YES qed support : YES parallels support : YES FUSE exports : NO VDUSE block exports : YES Crypto TLS priority : NORMAL GNUTLS support : NO libgcrypt : NO nettle : NO AF_ALG support : NO rng-none : NO Linux keyring : YES Linux keyutils : NO User interface SDL support : YES 2.0.10 SDL image support : YES 2.0.5 GTK support : YES pixman : YES 0.38.4 VTE support : NO PNG support : YES 1.6.37 VNC support : YES VNC SASL support : NO VNC JPEG support : YES 2.0.3 spice protocol support : NO curses support : YES brlapi support : NO Audio backends OSS support : YES sndio support : NO ALSA support : YES 1.2.2 PulseAudio support : YES 13.99.1 PipeWire support : NO JACK support : NO Network backends AF_XDP support : NO slirp support : YES 4.1.0 vde support : YES netmap support : NO l2tpv3 support : YES Dependencies libtasn1 : NO PAM : NO iconv support : YES virgl support : YES 0.8.2 rutabaga support : NO blkio support : NO curl support : NO Multipath support : NO Linux AIO support : NO Linux io_uring support : NO ATTR/XATTR support : YES RDMA support : NO PVRDMA support : NO fdt support : system libcap-ng support : NO bpf support : NO rbd support : NO smartcard support : NO U2F support : NO libusb : NO usb net redir : NO OpenGL support (epoxy) : YES 1.5.4 GBM : YES 21.2.6 libiscsi support : NO libnfs support : NO seccomp support : NO GlusterFS support : NO hv-balloon support : NO TPM support : YES libssh support : NO lzo support : NO snappy support : YES bzip2 support : NO lzfse support : NO zstd support : NO NUMA host support : NO capstone : NO libpmem support : YES 1.8 libdaxctl support : YES 67+ libudev : YES 245 FUSE lseek : NO selinux : YES 3.0 libdw : NO Subprojects berkeley-softfloat-3 : YES berkeley-testfloat-3 : YES keycodemapdb : YES libvduse : YES libvhost-user : YES User defined options Native files : config-meson.cross docs : disabled plugins : true

如果有一些功能没支持,我们需要通过pip/apt安装对于的开发包即可,这里就不详细说明了。

2.3 编译qemu

执行编译非常简单,我们直接make即可

make -j$(cat /proc/cpuinfo | grep processor | wc -l)

等待一会儿,qemu就完整编译好啦

2.4 安装

为了正常安装,我们执行make install即可,这里指定了destdir为out,如下

make install DESTDIR=out

为了使得qemu默认启用,我们需要定义PATH变量,如下:

export PATH=$PATH:$(pwd)/build/out/usr/local/bin/

此时,我们检查一下是否正常,如下:

qemu-system-aarch64 --version QEMU emulator version 8.2.7 Copyright (c) 2003-2023 Fabrice Bellard and the QEMU Project developers

至此,qemu正常运行

三、运行

对于windows,我们需要写一个powershell脚本,如下:

qemu-system-aarch64.exe ` -M virt ` -cpu cortex-a53 ` -smp 4 ` -m 8192 ` -nographic ` -drive if=none,file=./userdata.img,format=raw,id=userdata,index=0 ` -device virtio-blk-device,drive=userdata ` -drive if=none,file=./vendor.img,format=raw,id=vendor,index=2 ` -device virtio-blk-device,drive=vendor ` -drive if=none,file=./system.img,format=raw,id=system,index=1 ` -device virtio-blk-device,drive=system ` -drive if=none,file=./Image,format=raw,id=empty,index=3 ` -device virtio-blk-device,drive=empty ` -kernel ./Image ` -initrd ./ramdisk.img ` -vnc :20 ` -k en-us ` -net nic ` -net user,hostfwd=tcp::6666-:6666 ` -device virtio-gpu,xres=486,yres=864,max_outputs=1,addr=08.0 ` -display sdl,gl=on ` -device virtio-mouse-pci ` -device virtio-keyboard-pci ` -append "loglevel=7 ip=192.168.137.2:192.168.137.1:192.168.137.1:255.255.255.0::eth0:off LIBGL_ALWAYS_SOFTWARE=true LIBGL_DEBUG=verbose EGL_LOG_LEVEL=debug sn=0023456789 console=ttyAMA0,115200 init=/bin/init ohos.boot.hardware=arm64_virt default_boot_device=a003e00.virtio_mmio root=/dev/ram0 rw ohos.required_mount.system=/dev/block/vdb@/usr@ext4@ro,barrier=1@wait,required ohos.required_mount.vendor=/dev/block/vdc@/vendor@ext4@ro,barrier=1@wait,required ohos.required_mount.misc=/dev/block/vdd@/misc@none@none=@wait,required ohos.required_mount.data=/dev/block/vda@/data/@ext4@nosuid,nodev,noatime,barrier=1,data=ordered,noauto_da_alloc@wait,reservedsize=104857600 sn=0023456789"

对于Linux,我们直接复用openharmony的qemu-run脚本

./qemu-run board: arm64_virt Enter to start qemu[y/n]:

为了方便,也可以编写一个qemu脚本,如下内容

#!/bin/bash qemu-system-aarch64 \ -M virt \ -cpu cortex-a53 \ -smp 4 \ -m 8192 \ -nographic \ -drive if=none,file=out/arm64_virt/packages/phone/images/userdata.img,format=raw,id=userdata,index=3 \ -device virtio-blk-device,drive=userdata \ -drive if=none,file=out/arm64_virt/packages/phone/images/vendor.img,format=raw,id=vendor,index=2 \ -device virtio-blk-device,drive=vendor \ -drive if=none,file=out/arm64_virt/packages/phone/images/system.img,format=raw,id=system,index=1 \ -device virtio-blk-device,drive=system \ -drive if=none,file=out/arm64_virt/packages/phone/images/updater.img,format=raw,id=updater,index=0 \ -device virtio-blk-device,drive=updater \ -kernel out/arm64_virt/packages/phone/images/Image \ -initrd out/arm64_virt/packages/phone/images/ramdisk.img \ -vnc :20 \ -k en-us \ -net nic \ -net user,hostfwd=tcp::6666-:6666 \ -device virtio-gpu-gl,xres=486,yres=864 \ -display sdl,gl=on \ -append "loglevel=7 ip=192.168.137.2:192.168.137.1:192.168.137.1:255.255.255.0::eth0:off LIBGL_ALWAYS_SOFTWARE=true LIBGL_DEBUG=verbose EGL_LOG_LEVEL=debug sn=0023456789 console=ttyAMA0,115200 init=/bin/init ohos.boot.hardware=arm64_virt default_boot_device=a003e00.virtio_mmio root=/dev/ram0 rw ohos.required_mount.system=/dev/block/vdb@/usr@ext4@ro,barrier=1@wait,required ohos.required_mount.vendor=/dev/block/vdc@/vendor@ext4@ro,barrier=1@wait,required ohos.required_mount.misc=/dev/block/vda@/misc@none@none=@wait,required ohos.required_mount.data=/dev/block/vdd@/data/@ext4@nosuid,nodev,noatime,barrier=1,data=ordered,noauto_da_alloc@wait,reservedsize=104857600 sn=0023456789"

此时,我们可以看到qemu启动的鸿蒙界面如下:

image.png

编辑
2025-01-22
工作知识
0

最近在调研ota的基本功能,之前根据安卓的文章可以知道安卓基于dm框架实现了virtual ab system,这里在linux也能实现,所以调研了一下dmsetup的基本功能,为ota升级做准备。

一、安卓的虚拟ab功能

如果需要了解安卓的ab功能,可以如下链接:

https://source.android.com/docs/core/ota/virtual_ab

1.1 dm-linear

这里先将system分区抽象成dm-linear,这样system分区是super分区的多个块,如下

image.png

1.2 dm-snapshot

通过snapshot的基本功能,将系统分为origin和snapshot,这里origin是源设备分区,cow是写时复制分区,整个系统的快照由origin+COW组成,如下

image.png

二、Linux的调研

为了能够基于安卓的理念实现ota,这里需要借助工具dm-setup,如下是测试上述功能的基本命令

# create all device dmsetup create origin_device --table "0 $(blockdev --getsz /dev/mmcblk0p6) linear /dev/mmcblk0p6 0" dmsetup create cow_device --table "0 $(blockdev --getsz /dev/mmcblk0p4) linear /dev/mmcblk0p4 0" dmsetup create origin --table "0 $(blockdev --getsz /dev/mmcblk0p6) snapshot-origin /dev/mapper/origin_device" dmsetup create cow --table "0 $(blockdev --getsz /dev/mmcblk0p6) snapshot /dev/mapper/origin_device /dev/mapper/cow_device P 8" # merge dmsetup suspend origin dmsetup remove cow # merge cow_device--->origin_device dmsetup reload origin --table "0 $(blockdev --getsz /dev/mmcblk0p6) snapshot-merge /dev/mapper/origin_device /dev/mapper/cow_device P 8" dmsetup resume origin # merge to origin dmsetup suspend origin dmsetup reload origin --table "0 $(blockdev --getsz /dev/mmcblk0p6) snapshot-origin /dev/mapper/origin_device" dmsetup resume origin # create cow for snapshot dmsetup create cow --table "0 $(blockdev --getsz /dev/mmcblk0p6) snapshot /dev/mapper/origin_device /dev/mapper/cow_device P 8"

至此,一个基本基于dmsetup的快照功能调研完成,快照,合并的基本功能均正常

编辑
2025-01-22
工作知识
0

rk3568的oh版本烧录镜像有多个image,本文解释这些img的作用

一、总览

包含镜像如下

MiniLoaderAll.bin 瑞芯微平台的芯片级的romcode parameter.txt 瑞芯微平台分区表 resource.img 瑞芯微平台的资源文件,内部存放logo.bmp logo_kernel.bmp rk-kernel.dtb boot_linux.img ext2的boot分区,里面存放extlinux的image和dtb以及extlinux.conf,同时存放开机logo的bmp(logo.bmp logo_kernel.bmp) ramdisk.img oh系统的ramdisk文件,做二级启动 system.img oh系统的系统文件 vendor.img oh系统芯片相关厂商目录,vendor/{产品解决方案厂商}/{产品名称}定义的文件按照规则放入vendor分区 sys_prod.img oh系统定义的产品通用配置,对系统组件的能力扩展以及能力定制,承载产品级差异能力,存放产品相关的配置文件 chip_prod.img oh系统产品硬件配置组件,单板外设特有硬件能力以及产品级硬件差异配置, 存放芯片相关的配置文件 updater.img oh系统升级镜像,用于完成升级;正常启动时不加载次镜像,和ramdisk格式一致 eng_system.img oh系统的eng模式,利用mount --bind属性overlay文件 chip_ckm.img oh系统单独分区,用来支持内核树外独立构建ko模块能力 userdata.img oh系统可写的用户数据镜像

二、MiniLoaderAll.bin

/device/board/hihope/rk3568/loader目录中,通过/device/board/hihope/rk3568/kernel/build_kernel.sh拷贝到out/rk3568/packages/phone/images/

三、parameter.txt

同上也在/device/board/hihope/rk3568/loader中,通过/device/board/hihope/rk3568/kernel/build_kernel.sh拷贝到out/rk3568/packages/phone/images/

对于loader目录的文件,是通过/device/board/hihope/rk3568/BUILD.gn包含/device/board/hihope/rk3568/kernel/BUILD.gn中定义script = "build_kernel.sh"来运行build_kernel.sh的

对于parameter.txt内容,这里不详细解析,可以查看之前文章

四、resource.img

这个镜像内部包含文件logo.bmp logo_kernel.bmp rk-kernel.dtb

其中logo.bmp logo_kernel.bmp是开机uboot加载图片和内核加载图片,而rk-kernel.dtb是uboot阶段加载的默认dtb文件。瑞芯微平台可以将uboot加载的dtb和kernel加载的dtb放在同一个位置,但是oh下的rk3568没有这样做。所以需要resource.img放uboot加载的dtb,而boot_linux.img放内核通过extlinux加载的dtb

uboot加载dtb日志如下

dwmmc@fe2b0000: 1, dwmmc@fe2c0000: 2, sdhci@fe310000: 0 Bootdev(atags): mmc 0 MMC0: HS200, 200Mhz PartType: EFI DM: v1 boot mode: normal FIT: No boot partition A/B read part name is bootctrl current_slot_addr value is 0x52544e45 current slot is 1381256773 Found DTB in resource part DTB: rk-kernel.dtb

这里如果想解压resource.img,如下命令

../../OBJ/linux-5.10/scripts/resource_tool --verbose --unpack --image=../../OBJ/linux-5.10/resource.img

此时在当前目录的out下存在如下文件

logo.bmp logo_kernel.bmp rk-kernel.dtb

如果想自行打包resource.img,如下

../../OBJ/linux-5.10/scripts/resource_tool ../../OBJ/linux-5.10/arch/arm64/boot/dts/rockchip/rk3568-toybrick-x0-linux.dtb logo.bmp logo_kernel.bmp

此时当前目录下存在resource.img,直接烧录即可

注意:值得留意的是,resource_tool 打包时logo.bmp和logo_kernel.bmp不要带路径,否则启动时找不到

五、boot_linux.img

boot_linux.img是内核的镜像文件,这里是通过ext2打包,通过extlinux加载,uboot默认使能extlinux即可正常加载

boot_linux.img内文件如下

. ├── extlinux │ ├── extlinux.conf │ ├── Image │ └── toybrick.dtb ├── logo.bmp ├── logo_kernel.bmp

这里注意的是,logo.bmp和logo_kernel.bmp目前uboot并没有使用,所以此文件可以删掉,后续如果动态开机日志修改时,需要修改uboot,读取此分区的bmp文件才行。

toybrick.dtb是内核加载的设备树,Image是内核加载的二进制,uboot加载信息如下

Found /extlinux/extlinux.conf Retrieving file: /extlinux/extlinux.conf 203 bytes read in 4 ms (48.8 KiB/s) 1: rockchip-kernel-5.10 A/B read part name is bootctrl current_slot_addr value is 0x52544e45 current slot is 1381256773 Get a/b slot failed. assume device do not support A/B partition find partition ' ramdisk ' Load from partition ' ramdisk ' to address 0xa200000, count: 8192 block(s) Read from partition ' ramdisk ' done, total block(s) 8192 Openharmony ramdisk_addr_r = 0x0a200000:0x400000 find partition ' boot_linux ' Load from partition ' boot_linux ' to address 0x280000, count: 196608 block(s) Read from partition ' boot_linux ' done, total block(s) 196608 Retrieving file: /extlinux/Image 47439880 bytes read in 266 ms (170.1 MiB/s) append: earlycon=uart8250,mmio32,0xfe660000 root=PARTUUID=614e0000-0000-4b53-8000-1d28000054a9 rw rootwait rootfstype=ext4 Retrieving file: /extlinux/toybrick.dtb
编辑
2025-01-22
工作知识
0

openharmony的默认代码使用通过计算方式获取的vblank值,这个值是20ms,但是rk3588默认支持drmVBlank,所以可以直接使用libdrm的drmWaitVBlank接口等待每一个vblank的同步,从而提高ui的响应性能,如下是处理方式

一、vblank概念解释

通常的,我们知道一个显示屏有display timing参数,对于dsi屏幕,举例如下:

disp_timings0: display-timings { native-mode = <&dsi_timing0>; dsi_timing0: timing0 { clock-frequency = <72600000>; hactive = <800>; vactive = <1280>; hsync-len = <14>; hback-porch = <26>; hfront-porch = <32>; vsync-len = <8>; vback-porch = <20>; vfront-porch = <80>; hsync-active = <0>; vsync-active = <0>; de-active = <0>; pixelclk-active = <0>; }; };

这里我们可以计算出来屏幕的刷新率,如下公式

刷新率 = 时钟/((有效宽度+HSYNC宽+HBP+HFP)(有效高度+VSYNC宽+VBP+VFP))

这里的刷新率,即屏幕的刷新率,通常是60hz。也就是系统每刷新一帧图像需要的实现的倒数,即vsync值。

而对于linux而言,drm框架抽象了vsync的概念,将其称之为vblank,也就意味着,在linux平台上,可以通过drm的接口获取到当前真实硬件的每帧刷新时间。从而可以帮助系统软件gpu渲染时更好的对接到硬件屏幕。

二、rk平台的vblank

在瑞芯微平台,我们知道瑞芯微存在一个vop驱动,路径为drivers/gpu/drm/rockchip/rockchip_drm_vop2.c,这个vop驱动将显示图像放在vp buffer内,从而直接送往硬件,vop存在于drm框架的底层实现上。

关于vop2的中断函数如下:

static irqreturn_t vop2_isr(int irq, void *data) { ...... if (active_irqs & FS_FIELD_INTR) { rockchip_drm_dbg(vop2->dev, VOP_DEBUG_VSYNC, "vsync_vp%d\n", vp->id); vop2_wb_handler(vp); if (likely(!vp->skip_vsync) || (vp->layer_sel_update == false)) { drm_crtc_handle_vblank(crtc); vop2_handle_vblank(vop2, crtc); } active_irqs &= ~FS_FIELD_INTR; ret = IRQ_HANDLED; } }

关于vop2触发vblank的函数如下:

static void vop2_handle_vblank(struct vop2 *vop2, struct drm_crtc *crtc) { struct drm_device *drm = vop2->drm_dev; struct vop2_video_port *vp = to_vop2_video_port(crtc); unsigned long flags; spin_lock_irqsave(&drm->event_lock, flags); if (vp->event) { drm_crtc_send_vblank_event(crtc, vp->event); drm_crtc_vblank_put(crtc); vp->event = NULL; } spin_unlock_irqrestore(&drm->event_lock, flags); if (test_and_clear_bit(VOP_PENDING_FB_UNREF, &vp->pending)) drm_flip_work_commit(&vp->fb_unref_work, system_unbound_wq); }

根据drm_crtc_send_vblank_event drm_crtc_vblank_put 我们可以知道,他发送了vblank事件,这样如果上层应用程序调用到内核的drm_wait_vblank_ioctl就可以获取到每个vblank的时间。

所以,真实的vsync,应该是vop2驱动在发送vop2_isr的那一刻。

三、Openharmony当前策略

在代码device/soc/rockchip/rk3588/hardware/display/src/display_device/drm_display.cpp中,存在函数DrmDisplay::WaitForVBlank,此函数会被hdi层调用,其目前实现为

int32_t DrmDisplay::WaitForVBlank(uint64_t *ns) { DISPLAY_CHK_RETURN((ns == nullptr), DISPLAY_NULL_PTR, DISPLAY_LOGE("in ns is nullptr")); usleep(1000*20); *ns = 1000*1000*20; return DISPLAY_SUCCESS; }

在代码device/soc/rockchip/rk3588/hardware/display/src/display_device/drm_vsync_worker.cpp中,存在函数DrmVsyncWorker::WaitNextVBlank,如下

uint64_t DrmVsyncWorker::WaitNextVBlank(unsigned int &sq) { constexpr uint64_t SEC_TO_NSEC = 1000 * 1000 * 1000; struct timespec current; usleep(1000*20); sq = 1; clock_gettime(CLOCK_MONOTONIC, &current); return (uint64_t)(current.tv_sec * SEC_TO_NSEC + current.tv_nsec); }

WaitNextVBlank作为thread定期等待vsync信号来触发Vsync的callback,如下

void DrmVsyncWorker::WorkThread() { DISPLAY_LOGD(); unsigned int seq = 0; while (WaitSignalAndCheckRuning()) { // wait the vblank uint64_t time = WaitNextVBlank(seq); if (mCallBack != nullptr) { mCallBack->Vsync(seq, time); } else { DISPLAY_LOGE("the callbac is nullptr"); } } }

故可以发现,Openharmony默认使用计时等待的方式来等待vsync,而rk3588默认实际上可以支持vblank,则可以修改vblank,调用drm接口,使得显示支持vblank。

四、代码修改

根据上述代码分析,我们可以将openharmony默认的vblank修改为硬件等待的方式,从而使得系统更加流畅。主要修改代码如下

关于drm_display.cpp,如下

int32_t DrmDisplay::WaitForVBlank(uint64_t *ns) { int ret; constexpr uint64_t nPerS = 1000000000; constexpr uint64_t nPerUS = 1000; drmVBlank vbl = { .request.type = DRM_VBLANK_RELATIVE, .request.sequence = 0, .request.signal = 0, }; DISPLAY_CHK_RETURN((ns == nullptr), DISPLAY_NULL_PTR, DISPLAY_LOGE("in ns is nullptr")); ret = drmWaitVBlank(mDrmDevice->GetDrmFd(), &vbl); DISPLAY_CHK_RETURN((ret != 0), DISPLAY_FAILURE, DISPLAY_LOGE("wait vblank failed errno %{public}d", errno)); *ns = static_cast<uint64_t>(vbl.reply.tval_sec * nPerS + vbl.reply.tval_usec * nPerUS); return DISPLAY_SUCCESS; }

关于drm_vsync_worker.cpp,如下

uint64_t DrmVsyncWorker::WaitNextVBlank(unsigned int &sq) { constexpr uint64_t SEC_TO_NSEC = 1000 * 1000 * 1000; constexpr uint64_t USEC_TO_NSEC = 1000; drmVBlank vblank = { .request = drmVBlankReq { .type = DRM_VBLANK_RELATIVE, .sequence = 1, .signal = 0, } }; /* The drmWaitVBlank need set the crtc pipe when there are multi crtcs in the system. */ if (mCallBack->GetPipe() == 1) vblank.request.type = drmVBlankSeqType((int)(vblank.request.type) | (int)DRM_VBLANK_SECONDARY); int ret = drmWaitVBlank(mDrmFd, &vblank); DISPLAY_CHK_RETURN((ret < 0), 0, DISPLAY_LOGE("wait vblank failed ret : %{public}d errno %{public}d", ret, errno)); sq = vblank.reply.sequence; return (uint64_t)(vblank.reply.tval_sec * SEC_TO_NSEC + vblank.reply.tval_usec * USEC_TO_NSEC); }

完整补丁如 0001-rk3588-drm-vblank-wait.patch

五、编译验证

完成代码修改后,需要编译验证,如下是编译脚本

prebuilts/build-tools/linux-x86/bin/ninja -w dupbuild=warn -C out/rk3588/ display_composer_vendor mount /root/tf/rk3588/out/rk3588/packages/phone/images/vendor.img /tmp/temp/ md5sum /tmp/temp/lib64/libdisplay_composer_vendor.z.so cp /root/tf/rk3588/out/rk3588/hdf/rockchip_products/libdisplay_composer_vendor.z.so /tmp/temp/lib64/libdisplay_composer_vendor.z.so md5sum /tmp/temp/lib64/libdisplay_composer_vendor.z.so umount /tmp/temp/

编译后,更新烧录vendor.img即可

编辑
2025-01-22
工作知识
0

开发Openharmony的时候,经常需要调试Openharmony的hdi层,这层对上是对接UI,对下对接内核hdf,所以经常需要通过hilog命令来进行开发调试,本文根据display框架来讲解hilog的使用技巧

一、hilog概述

hilog是openharmony的日志系统,我们可以等价的理解为android的logcat,对于hilog而言,只要系统启动了hilogd,则hilog可以正常工作,如下

# ps -A | grep hilogd 281 ? 00:01:03 hilogd

hilog的架构图如下

image.png

二、hilog使用说明

对于hilog使用,这里总结有两种方式

  1. hilog实时抓取日志
  2. hilog记录和保存日志

2.1、 hilog实时抓取日志

直接运行hilog即可抓取当前系统的hilog日志,如下

# hilog 04-18 08:52:57.711 0 0 I I00000/HiLog: ========Zeroth log of type: init

可以留意的是,hilog是将缓存的日志打印出来并阻塞等待新的日志信息

如果相对hilog日志做区分,可以将hilog日志分为init,app,core。分别对应启动日志,应用日志,核心(HDI)日志,如下:

# hilog -t init 04-18 08:52:57.711 0 0 I I00000/HiLog: ========Zeroth log of type: init

hilog日志存在日志等级,info,debug,error, fault。如果我们需要查看指定等级的日志可以如下:

hilog -L E 04-18 10:57:23.289 776 776 E C02f01/AccessTokenInfoManager: [GetNativeTokenInfo]:token 537715846 is invalid.

hilog默认关闭了debug等级的日志, 所以如果需要打开debug级别的日志,则需要单独设置hilog的输出日志等级,如下

# hilog -b D Set global log level to D successfully # hilog -L D 04-18 10:59:00.008 1285 1285 D A0001a/SystemUI_Default: CommonEvent_TimeManager --> handle common event: usual.event.TIME_TICK

hilog可以指定模块tag的日志进行显示,从而达到过滤的作用,可以通过如下指定只查看DISP显示模块的日志

# hilog -T DISP 04-18 11:00:00.063 746 752 D C02500/DISP: [CallLayerFunction:51] device Id : 1

hilog可以给日志带上颜色,这样我们可以很方便的通过颜色来判断什么日志是我们需要关心的,如下

hilog -T DISP -v color 04-18 11:02:00.051 746 752 D C02500/DISP: [CallLayerFunction:51] device Id : 1 04-18 11:02:00.051 746 752 D C02500/DISP: [GetHdiLayer:214] id : 78

hilog可以指定pid来查看日志,如果我们知道服务是哪个进程启动的,则可以如下

# hilog -P `pidof composer_host` 04-18 11:04:08.816 746 752 D C02500/DISP: [CallDisplayFunction:39] device Id : 1

hilog可以过来日志信息中的关键字,如果我们对日志内部有关键字标识,可以如下

hilog -e failed 04-18 11:07:30.432 1328 1343 E C03900/Ace: [<private>(<private>)] connect failed errno:111

2.2、hilog记录和保存日志

通常情况下,hilog可以抓取大部分的日志,但是对于开机日志,我们只能通过hilog日志保存到文件上,重新开机来抓取这次开机的错误日志信息来判断问题,所以需要如下设置

hilog -w stop hilog -w start -m none -f display -l 20M hilog -b D rm /data/log/hilog/* reboot

此时hilog打开了日志保存文件的功能,文件默认保存在/data/log/hilog/display.*下,此时我们重启,则可以将重启的hilog日志进行导出,此时导出日志需要利用hdc命令,hdc命令是类似于android的adb命令。故如下

hdc file recv /data/log/hilog/ .

这样我们可以将机器的hilog日志正常导出出来

三、hilog的代码填写示例

对于hilog日志,不同的模块通过不同的封装来实现,我以display模块为例。对于打印display模块的hilog日志,可以如下

DISPLAY_LOGD("kylin: set crtc to active");

可以发现DISPLAY_LOGD是一个宏定义,如下

#ifndef DISPLAY_LOGI #define DISPLAY_LOGI(format, ...) \ do { \ HILOG_INFO(LOG_CORE, "[%{public}s@%{public}s:%{public}d] " format "\n", __FUNCTION__, FILENAME, __LINE__, \ ##__VA_ARGS__); \ } while (0) #endif

可以知道其定义是HILOG_INFO,在base/hiviewdfx/hilog/interfaces/native/innerkits/include/hilog/log_c.h定义如下

#define HILOG_INFO(type, ...) ((void)HILOG_IMPL((type), LOG_INFO, LOG_DOMAIN, LOG_TAG, __VA_ARGS__)) 对于HILOG_IMPL定义如下 #define HILOG_IMPL(type, level, domain, tag, ...) HiLogPrint(type, level, domain, tag, ##__VA_ARGS__)

对于HiLogPrint定义如下

int HiLogPrint(LogType type, LogLevel level, unsigned int domain, const char *tag, const char *fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = HiLogPrintArgs(type, level, domain, tag, fmt, ap); va_end(ap); return ret; }

故,大家在开发openharmony时,注意好日志的宏设计即可