编辑
2025-03-03
工作知识
0

RK最近实现了从上层处理的硬件光标层,仓库地址为 https://github.com/JeffyCN/drm-cursor 。以往的如RK3399的硬件光标都是走的libdrm的通用接口,但在新的平台例如RK3568和RK3588上,内核貌似没有单独设置鼠标层。

如果需要在平台上使用硬件光标,可以使用这个库实现。

这里实现的drm curosr 鼠标指针,是通过hook的方式,把drm设置鼠标的相关函数通过EGL的接口实现,避免通过libdrm走到内核上面去。

关于什么是光标,这里给出了解释:

Cursors Similar to planes, many hardware also supports cursors. A cursor is a very small buffer with an image that is blended over the CRTC framebuffer. You can set a different cursor for each CRTC with drmModeSetCursor(3) and move it on the screen with drmModeMoveCursor(3). This allows to move the cursor on the screen without rerendering. If no hardware cursors are supported, you need to rerender for each frame the cursor is moved.

一:标准光标函数实现

1.1 libdrm函数实现

drmModeSetCursor xf86drmMode.c +412 drm_public int drmModeSetCursor(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height) { struct drm_mode_cursor arg; memclear(arg); arg.flags = DRM_MODE_CURSOR_BO; arg.crtc_id = crtcId; arg.width = width; arg.height = height; arg.handle = bo_handle; return DRM_IOCTL(fd, DRM_IOCTL_MODE_CURSOR, &arg); }
DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER),
drmModeMoveCursor drm_public int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y) { struct drm_mode_cursor arg; memclear(arg); arg.flags = DRM_MODE_CURSOR_MOVE; arg.crtc_id = crtcId; arg.x = x; arg.y = y; return DRM_IOCTL(fd, DRM_IOCTL_MODE_CURSOR, &arg); }
DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER),
drmModeSetCursor2 drm_public int drmModeSetCursor2(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y) { struct drm_mode_cursor2 arg; memclear(arg); arg.flags = DRM_MODE_CURSOR_BO; arg.crtc_id = crtcId; arg.width = width; arg.height = height; arg.handle = bo_handle; arg.hot_x = hot_x; arg.hot_y = hot_y; return DRM_IOCTL(fd, DRM_IOCTL_MODE_CURSOR2, &arg); }
DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_UNLOCKED)

1.2 Xorg函数实现

对应Xorg中实现光标的地方如下

hw/xfree86/drivers/modesetting/drmmode_display.c
drmmode_set_cursor_position
static void drmmode_set_cursor_position(xf86CrtcPtr crtc, int x, int y) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; drmmode_ptr drmmode = drmmode_crtc->drmmode; drmModeMoveCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, x, y); }
drmmode_show_cursor
static Bool drmmode_show_cursor(xf86CrtcPtr crtc) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; drmmode_crtc->cursor_up = TRUE; return drmmode_set_cursor(crtc); }

1.3 是否使用硬件光标

ret = drmModeSetCursor2(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, handle, ms->cursor_width, ms->cursor_height, cursor->bits->xhot, cursor->bits->yhot); /* -EINVAL can mean that an old kernel supports drmModeSetCursor but * not drmModeSetCursor2, though it can mean other things too. */ if (ret == -EINVAL) ret = drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, handle, ms->cursor_width, ms->cursor_height); /* -ENXIO normally means that the current drm driver supports neither * cursor_set nor cursor_set2. Disable hardware cursor support for * the rest of the session in that case. */ if (ret == -ENXIO) { xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); xf86CursorInfoPtr cursor_info = xf86_config->cursor_info; cursor_info->MaxWidth = cursor_info->MaxHeight = 0; drmmode_crtc->drmmode->sw_cursor = TRUE; }

如果内核的IOCTL为 -ENXIO 则Xorg实现软光标

drmmode_crtc->drmmode->sw_cursor = TRUE;

二:RK的光标的钩子实现

2.1 函数实现

int drmModeSetCursor(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height) { /* Init log file */ drm_get_ctx(fd); return drm_set_cursor(fd, crtcId, bo_handle, width, height); } int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y) { return drm_move_cursor(fd, crtcId, x, y); } int drmModeSetCursor2(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y) { return -EINVAL; }

2.2 钩子的处理

这个仓库最终生成了 libdrm-cursor.so.1.0.0 的库文件,但是光标的实现函数在libdrm的so里面。这里RK使用了ld的preload的配置

方式是在debian/libdrm-cursor.postinst 内添加 echo /usr/lib/*/libdrm-cursor.so.1 >> /etc/ld.so.preload

Xorg的链接情况如下,说明钩子已生效

kylin@kylin:~$ ldd /usr/lib/xorg/Xorg | grep drm /usr/lib/aarch64-linux-gnu/libdrm-cursor.so.1 (0x0000007fb9a31000) libdrm.so.2 => /lib/aarch64-linux-gnu/libdrm.so.2 (0x0000007fb97d1000)

2.3 drm-cursor运行日志

drm_get_ctx(485) atomic drm API enabled drm_get_ctx(518) max fps: 60 drm_get_ctx(559) found 0 CRTC: 64(0) (1920x1200) prefer plane: 0 drm_get_ctx(559) found 1 CRTC: 83(1) (0x0) prefer plane: 0 drm_get_ctx(567) found 2 CRTCs drm_get_ctx(616) found plane: 58[primary] crtcs: 0x1 (ARGB) drm_get_ctx(616) found plane: 61[cursor ] crtcs: 0x1 (ARGB) drm_get_ctx(616) found plane: 65[overlay] crtcs: 0x1 (ARGB) drm_get_ctx(616) found plane: 68[overlay] crtcs: 0x1 (ARGB) drm_get_ctx(616) found plane: 80[primary] crtcs: 0x2 (ARGB) drm_get_ctx(616) found plane: 84[overlay] crtcs: 0x2 (ARGB) drm_get_ctx(625) using libdrm-cursor (1.3.0~20211201) drmModeSetCursor(1185) fd: 11 crtc: 64 handle: 0 size: 0x0 drm_crtc_bind_plane(692) CRTC[64]: using cursor plane drm_crtc_bind_plane(700) CRTC[64]: bind plane: 61 drm_set_cursor(1097) CRTC[64]: request setting new cursor 0 (0x0) drm_crtc_thread_fn(866) CRTC[64]: thread started drm_crtc_thread_fn(924) CRTC[64]: set new cursor 0 (0x0) drmModeMoveCursor(1192) fd: 11 crtc: 64 position: 956,596 drm_move_cursor(1154) CRTC[64]: request moving cursor to (956,596) in (1920x1200)

可以看出的信息为,1.解析配置文件,2.找到drm支持的crtc,3.找到对应的plane,4.将配置指定的plane配置为光标使用的plane

2.4 内核运行日志

查看内核drm光标这块的日志,需要打开drm相关的配置宏

drm的sys节点为: /sys/module/drm/parameters/debug

对应的可选值如下:

Bit 0 (0x01) will enable CORE messages (drm core code) Bit 1 (0x02) will enable DRIVER messages (drm controller code) Bit 2 (0x04) will enable KMS messages (modesetting code) Bit 3 (0x08) will enable PRIME messages (prime code) Bit 4 (0x10) will enable ATOMIC messages (atomic code) Bit 5 (0x20) will enable VBL messages (vblank code) (int)

因为光标通过atomic的api实现的,通过借助本身调试atomic的debug,可以提供一点有用的信息。

echo 0x10 > /sys/module/drm/parameters/debug 打开drm ATOMIC 调试信息

日志如下:

[drm:drm_atomic_state_init] Allocated atomic state 000000009ea89924 [drm:drm_atomic_get_plane_state] Added [PLANE:71:Cluster0-win0] 000000001d2516b5 state to 000000009ea89924 [drm:drm_atomic_get_crtc_state] Added [CRTC:85:video_port0] 000000002526233c state to 000000009ea89924 [drm:drm_atomic_set_fb_for_plane] Set [FB:202] for [PLANE:71:Cluster0-win0] state 000000001d2516b5 [drm:drm_atomic_check_only] checking 000000009ea89924 [drm:drm_atomic_commit] committing 000000009ea89924 [drm:drm_atomic_state_default_clear] Clearing atomic state 000000009ea89924 [drm:__drm_atomic_state_free] Freeing atomic state 000000009ea89924

可以发现,默认使用plane 71,crtc 85,fb202

查看sys下的信息,是否正常对应

1.cat /sys/kernel/debug/dri/0/framebuffer | grep drm-cursor -B 1
framebuffer[202]: allocated by = drm-cursor[85]
2.cat /sys/kernel/debug/dri/0/state | grep drm-cursor -B 3
plane[71]: Cluster0-win0 crtc=video_port0 fb=208 allocated by = drm-cursor[85]
3.cat /sys/kernel/debug/dri/0/summary
Cluster0-win0: ACTIVE win_id: 4 format: AB24 little-endian (0x34324241)[AFBC] SDR[0] color_space[0] glb_alpha[0xff] rotate: xmirror: 0 ymirror: 0 rotate_90: 0 rotate_270: 0 csc: y2r[0] r2y[1] csc mode[1] zpos: 7 src: pos[0, 0] rect[64 x 64] dst: pos[0, 0] rect[64 x 64] buf[0]: addr: 0x0000000001825000 pitch: 256 offset: 0

由此,可以确定,硬件光标正常启动,大小为64x64

三:测试和调试

3.1 配置文件 /etc/drm-cursor.conf

# debug=1 # log-file= # hide=1 # hide cursors # atomic=0 # disable atomic drm API # max-fps=60 # allow-overlay=1 # allowing overlay planes # prefer-afbc=0 # prefer plane with AFBC modifier supported # num-surfaces=8 # num of egl surfaces to avoid edge moving corruption # prefer-plane=65 # prefer-planes=61,65 # crtc-blocklist=64,83
debug=1 默认会将log输出在 /var/log/drm-cursor.log log-file=/tmp/drm-cursor.log 手动指定log文件 hide=1 是否隐藏鼠标 atomic=1 这一项没什么用,drm cursor默认使用atomic 的api max-fps=60 光标指针的显示帧数。 allow-overlay=1 允许使用overlay层。如果不设置,则默认不使用overlay,也就是只使用cursor层 prefer-afbc=0 是否使用arm的 afbc功能的overlay层 num-surfaces=8 eglCreateWindowSurface 创建的离屏缓冲区的数量 prefer-plane=65 默认设置的plane prefer-planes=61,65 可选的多个plane crtc-blocklist=83 阻止在哪些crtc上允许光标层

3.2 如何选择plane和crtc

安装libdrm-tests

modeprint rockchip Crtc id : 64 x : 0 y : 0 width : 1920 height : 1200 mode : 0x55ad634f8c gamma size : 1024

可以看到当前显示挂的crtc在64上

modetest -M rockchip -p 查看plane 61 64 117 0,0 0,0 0 0x00000001 formats: XR24 AR24 XB24 AB24 RG24 BG24 RG16 BG16 props: 7 type: flags: immutable enum enums: Overlay=0 Primary=1 Cursor=2 value: 2 8 SHARE_ID: flags: immutable range values: 0 4294967295 value: 61 63 rotation: flags: bitmask values: rotate-0=0x1 reflect-y=0x20 value: 1 56 FEATURE: flags: immutable bitmask values: scale=0x1 alpha=0x2 hdr2sdr=0x4 sdr2hdr=0x8 afbdc=0x10 pdaf_pos=0x20 value: 18

可以看到plane 61 是挂载crtc64上的cursor层

测试硬件光标验证:

systemctl stop lightdm modetest -M rockchip -s 91@64:1920x1200 -C

如果出现颜色图,则说明libdrm能够正常使用硬件光标

指定plane

modetest -M rockchip -P 61@64:1920x1200

如果出现颜色图,则说明libdrm能够正常指定plane

四:具体应用

首先需要在内核打开硬件光标层,找到你需要配置硬光标的vp,添加 cursor-win-id,例如我的显示是在vp2上面

image.png

image.png

然后需要安装libdrm-cursor软件和libdrm-tests软件

首先使用libdrm-tests软件查看实际使用中的crtc层

命令:modetest

image.png

image.png

image.png

image.png 这里可以看到68、85、102是未使用的crtc,实际连接到fb正在使用的crtc是133

所以我们需要在软件中配置屏蔽掉前面三个crtc层,让鼠标层能够正确匹配到133上面

命令:vim /etc/drm-cursor.conf

修改前

image.png 修改后

image.png

这里我们把硬光标层打开了,所以就不需要overlay层,把它注释掉,然后打开crtc-blocklist选项,把其他不用的ctrc层屏蔽掉,重启机器就能正常使用硬件光标了。crtc层具体的id需要根据实际情况使用modetest命令确定。

五:参考链接

《Linux DRM Developer's Guide》https://landley.net/kdocs/htmldocs/drm.html

《Linux Kernel Doc》 https://www.kernel.org/doc/

《DRM-wikipedia》 https://en.wikipedia.org/wiki/Direct_Rendering_Manager 《Linux GPU Driver Developer's Guide》https://01.org/linuxgraphics/gfx-docs/drm/gpu/index.html

《DRM-KMS Doc》 https://kernel.readthedocs.io/en/latest/gpu/drm-kms.html#mode-setting 《KMS简介》 https://events.static.linuxfound.org/sites/events/files/slides/brezillon-drm-kms.pdf

《EGL Reference Pages》https://www.khronos.org/registry/EGL/sdk/docs/man/

《Linux User and Programmer's Manual》https://www.systutorials.com/docs/linux/man/3-drmModeGetResources/

《Nvidia drm api参考》https://docs.nvidia.com/jetson/l4t-graphics/group__direct__rendering__manager.html

《Linux How to的KMS简介》https://www.linuxhowtos.org/manpages/7/drm-kms.htm

《freedesktop.org的kms文档》https://dri.freedesktop.org/docs/drm/gpu/drm-kms.html

编辑
2025-03-03
工作知识
0

在RK的sdk中有制作update的工具二进制,但是这些二进制是只能运行在x86平台上。而我们的工作环境大部分情况下是在arm64上的,本篇文章提供了在arm64下能够打包update的方法。

一:获取源码

https://gitlab2.kylin.com/shanghai-team/create-rootfs/-/tree/kybuild/tool/src

如上是工具的源码,可以通过make的方式在各个平台上编译生成afptool和img_maker两个二进制

二:使用说明

目录附带了一个mkupdate.sh脚本,通过此脚本可以自动打包固件,如下为脚本内容

#!/bin/bash # Self Test CHIP=$1 usage() { echo "Usage: ./$(basename $0) rk3399 ./$(basename $0) rk3568 ./$(basename $0) rk3588" } if ! [ "${CHIP}" = "rk3399" ] && ! [ "${CHIP}" = "rk3568" ] && ! [ "${CHIP}" = "rk3588" ] then echo -e "\e[32mUnsupported Chip\e[0m" usage exit fi [ ! -d ./Image ] && echo "Cannot found Image directory" && exit ./afptool -pack ./ firmware_afp.img <<! # rk3399 ./img_maker -rk3399 ./Image/MiniLoaderAll.bin firmware_afp.img update.img # rk3568 ./img_maker -rk3588 ./Image/MiniLoaderAll.bin firmware_afp.img update.img # rk3588 ./img_maker -rk3568 ./Image/MiniLoaderAll.bin firmware_afp.img update.img ! ./img_maker -${CHIP} ./Image/MiniLoaderAll.bin firmware_afp.img update.img

2.1 打包文件package-file

在mkupdate.sh的当前目录,需要存在一个打包文件,里面描述了需要打包固件的名字和路径,该文件有自行编写,示例如下:

package-file package-file bootloader Image/MiniLoaderAll.bin parameter Image/parameter.txt trust Image/trust.img uboot Image/uboot.img boot Image/boot.img rootfs Image/rootfs.img

上述描述了需要打包的内容如,自身package-file,bootloader,分区表parameter,trust固件,uboot固件,内核固件boot,根文件系统rootfs

2.2 Image目录

mkupdate.sh需要一个Image目录,其中存放打包中需要的文件,我们要将需要打包的二进制手动拷贝到当前目录的Image目录内后方可运行测试脚本,其文件对应package-file的指定内容

2.3 parameter文件

分区文件主要指定了固件的分区细节,该文件根据平台自定义,rk3588的分区文件示例如下

FIRMWARE_VER: 1.0 MACHINE_MODEL: RK3588 MACHINE_ID: 007 MANUFACTURER: RK3588 MAGIC: 0x5041524B ATAG: 0x00200800 MACHINE: 0xffffffff CHECK_MASK: 0x80 PWR_HLD: 0,0,A,0,1 TYPE: GPT CMDLINE: mtdparts=rk29xxnand:0x00002000@0x00004000(uboot),0x00040000@0x00008000(boot:bootable),0x01508000@0x00048000(rootfs),0x00600000@0x01550000(data),0x00600000@0x01b50000(work),-@0x02150000(userdata:grow) uuid:rootfs=614e0000-0000-4b53-8000-1d28000054a9 uuid:boot=7A3F0000-0000-446A-8000-702F00006273

一般情况下只修改分区文件的cmdline中的分区地址,其他无需改变

2.4 输出文件

mkupdate.sh会在本地目录生成一个update.img,该文件即rk平台的固件格式,可以通过RK的工具进行整包烧录

2.5 烧录

https://gitlab2.kylin.com/shanghai-team/create-rootfs/-/tree/kybuild/tool/src/windows-tool

上述提供了2.92版本的烧录工具,我们自行开发的arm64的打包工具在此版本正常工作,其他版本尚未验证,无法保障。因为工具为自行修改,和RK的实现不一致,故可能出现windows烧录工具的版本不一样导致出现的烧录异常问题。

如RK更新了超过2.92版本的烧录工具,后续我们会再自行验证和兼容。

编辑
2025-03-03
工作知识
0

随着AI智能的发展,继传统的CPU和GPU之后,又推出了许多更适用于AI学习的芯片,本文就NPU芯片如何在RK平台提供的SDK基础上开发做一个介绍。

一、NPU芯片简介

NPU全称为Neural network Processing Unit,即神经网络处理器。

二、RKNN使用方法

1.SDK下载及内容介绍

可以在110服务器获取 /home/develop/rk/project/Rockchip_RK356X_RKNN_SDK_V0.7.0_20210402

这里 RKNN_API_for_RK356X 里面主要包含了linux和android系统对于如何使用模型文件调用NPU进行AI运算的demo。

rknn-toolkit 是用于将其他神经网络训练架构模型如何转化为 rknn 架构模型的demo,它分为了docker和nodocker版本,建议使用docker版,因为nodeocker版本对于python-3和pip软件版本的要求比较严格,环境不太好搭建。

2.rknn-toolkit 工具使用

这里我使用含docker版本举例。

  • 首先进入rknn-toolkit2-0.7.0/docker/ 文件夹
  • 可以看到 rknn-toolkit2-0.7.0-docker.tar.gz 镜像文件
  • 使用 sudo docker load --input rknn-toolkit2-0.7.0-docker.tar.gz 命令加载镜像
  • 加载成功后,可以使用 sudo docker images 命令确认。

正常加载后,就可以进入docker环境 如下,

docker run -t -i --privileged -v /home/kylin/:/home/kylin/ rknn-toolkit2:0.7.0 /bin/bash

执行后命令行的前缀会改变。

进入docker环境后,进入 rknn-toolkit2-0.7.0/examples/ 目录下,可以看到包含了一些以比较常见的神经网络训练框架命名的文件夹。

这里我们以caffe为例,将 caffemodule 转换为 rknnmodule。

进入 caffe/vgg-ssd/ 文件夹,其是转换 caffemodule 的一个demo 。里面使用了 deploy_rm_detection_output.prototxt 文件中提供的图层模型,把 caffemodule 通过 rknn.api 转换为了 rknnmodule 。重点包含以下几个函数:

image.png

image.png 可以看到流程和RK给出的文档中一致,按照步骤操作即可。

这里会需要一个 caffemodule 的训练模型,可以通过

https://eyun.baidu.com/s/3jJhPRzo , password is rknn

进行下载。

然后执行 python test.py 运行脚本,执行成功后就可以看到文件夹中多了 .rknn 为后缀的文件,它就是rk平台的模型。 这里就完成了模型的转换,过程中可能会根据模型效率/准确度的需求修改一些参数,可以参考SDK中的文档进行调整。

3.RKNN模型使用

上面我们已经得到了后缀为 .rknn 的模型文件,现在我们就来讲解如何在RK平台使用模型通过NPU进行图像的解析操作。其实上面在转换模型时已经使用python脚本进行了图像解析的示例,本章介绍demo中通过C语言调用RKNN接口的过程。

进入 RKNN_API_for_RK356X_v0.7_20210402/examples/rknn_ssd_demo/ 文件夹,可以看到 我们先看看源码调用流程

image.png 跟文档流程一致,其中多了一步 postProcessSSD ,这是将结果中的值转换为图片中的坐标点,用于后面画框所用。

image.png 看完了源代码我们可以编译应用。这里的环境是rk3568开发板+麒麟v10系统,所以直接执行 build-linux.sh 脚本。

image.png 完成后可以看到生成了 rknn_ssd_demo 的二进制文件,接下来我们使用它看看效果。

image.png 执行

./build/build_linux_aarch64/rknn_ssd_demo ./model/ssd_inception_v2.rknn ./model/road.bmp

第一个参数是我们生成的二进制文件
第二个参数是 .rknn 为后缀的module模型文件
第三个参数是要被检测的图片。执行后在本地文件夹中生成了 out.jpg 图片
输出如下 image.png

image.png 可以看到,模型在上图中成功识别到了行人和车辆的位置。

三、NPU硬件信息查看

如何判断是否调用到了NPU去处理的上面图像呢。我们可以通过中断:

image.png 如果调用到NPU,中断号会增加。

内核中还提供了调整NPU频率的接口:

image.png 查看NPU支持的模式,默认为 simple_ondemand

image.png 查看NPU当前的频率

echo performance > /sys/devices/platform/fde40000.npu/devfreq/fde40000.npu/governor

调整NPU的工作模式为性能模式(保持最大频率)

image.png 查看NPU当前工作状态,@之前为占用率,之后为当前频率

image.png 根据内核log,可以判断NPU是否被加载成功,从中也可以看出NPU包含了iommu模块,对内存读写进行了优化。

编辑
2025-03-03
工作知识
0

瑞芯微平台通常通过烧录的方式来更新系统,但是在一些项目评估的过程中,客户经常需要要求能够安装系统,为了让RK平台支持U盘安装,需要稍微在uboot进行修改,使得uboot阶段能够正常的加载到U盘的uImage,uInitrd,dtb文件即可。主要介绍如下

一、 文件列表

image.png 这里是U盘安装内U盘文件内容,主要分为

boot目录 内包含uboot启动所需要的uImage和uInitrd以及sysboot启动需要的extlinux.conf。主要用于启动安装系统 casper目录 内包含rockchip平台所需要的镜像文件 logo.bmp和logo_kernel.bmp 是安装中所需要的开机图片,如果需要使用其他开机图片,可以单独修改替换此图片即可 autorun.inf 和 kylin.ico 是U盘自动运行文件和U盘图标文件

1.1、extlinux.conf

文件内容如下

default install menu title Installer prompt 0 timeout 10 label install menu label kylinos installer linux /boot/uImage initrd /boot/uInitrd append console=tty0 loglevel=0

这里设置默认启动了uImage和uInitrd

1.2、 parameter-kylin.txt

文件内容如下

FIRMWARE_VER: 1.0 MACHINE_MODEL: RK3588 MACHINE_ID: 007 MANUFACTURER: RK3588 MAGIC: 0x5041524B ATAG: 0x00200800 MACHINE: 0xffffffff CHECK_MASK: 0x80 PWR_HLD: 0,0,A,0,1 TYPE: GPT CMDLINE: mtdparts=rk29xxnand:0x00004000@0x00004000(uboot),0x00002000@0x00008000(misc),0x00080000@0x0000a000(boot:bootable),0x00800000@0x0008a000(backup),0x01400000@0x0088a000(rootfs),-@0x01c8a000(userdata:grow) uuid:rootfs=614e0000-0000-4b53-8000-1d28000054a9

这里是安装过程中,需要对系统分区的描述文件。同RK平台的parameter.txt文件

1.3、 uboot-usb.img

这是支持机器的uboot通过默认usb启动的uboot镜像,如果需要使用U盘安装,则需要更新原先uboot镜像为此镜像。此镜像基于uboot额外做了如下措施

uboot内置设备树,确保uboot下usb功能正常 uboot内默认usb启动优先 uboot内默认支持u盘内开机图片显示

1.4、 idblock.bin/rk3588_spl_loader_xxx.bin

这是RK平台的loader 二进制文件,这里需要放置两个文件,安装时是用的idblock.bin,烧录的时候是用的rk3588_spl_loader_xxx.bin。

二、 启动修改点

2.1 uboot配置

diff --git a/configs/rockchip-linux.config b/configs/rockchip-linux.config index 3003d81..cee40ce 100644 --- a/configs/rockchip-linux.config +++ b/configs/rockchip-linux.config @@ -5,3 +5,6 @@ CONFIG_DM_PCA953X=y CONFIG_SPL_FIT_IMAGE_KB=4096 CONFIG_CHECK_VERSION_CHOOSE_DTB=y CONFIG_PHY_ROCKCHIP_NANENG_COMBOPHY=y +CONFIG_EMBED_KERNEL_DTB=y +CONFIG_EMBED_KERNEL_DTB_PATH="dts/rockchip-evb_rk3588.dtb" +CONFIG_EMBED_KERNEL_DTB_ALWAYS=n 2.2 开机显示特定logo diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h index 9ed6b98..c1ec5e2 100644 --- a/include/config_distro_bootcmd.h +++ b/include/config_distro_bootcmd.h @@ -29,6 +29,7 @@ #define BOOTENV_SHARED_BLKDEV_BODY(devtypel) \ "if " #devtypel " dev ${devnum}; then " \ "setenv devtype " #devtypel "; " \ + "rockchip_show_logo;" \ "run scan_dev_for_boot_part; " \ "fi\0"

2.3 默认启动usb

diff --git a/include/configs/rockchip-common.h b/include/configs/rockchip-common.h index 002dcd6..0566ece 100644 --- a/include/configs/rockchip-common.h +++ b/include/configs/rockchip-common.h @@ -162,7 +162,14 @@ "setenv devtype ramdisk; setenv devnum 0;" \ "fi; \0" -#if defined(CONFIG_AVB_VBMETA_PUBLIC_KEY_VALIDATE) +#if defined(CONFIG_EMBED_KERNEL_DTB) +#define RKIMG_BOOTCOMMAND \ + "run usb_boot;" \ + "boot_android ${devtype} ${devnum};" \ + "boot_fit;" \ + "bootrkp;" \ + "run distro_bootcmd;" +#elif defined(CONFIG_AVB_VBMETA_PUBLIC_KEY_VALIDATE)

2.4 字符终端字体

修改: drivers/tty/vt/selection.c 修改: drivers/tty/vt/vt.c 修改: drivers/video/fbdev/core/bitblit.c 修改: drivers/video/fbdev/core/fbcon.c 修改: drivers/video/fbdev/core/fbcon.h 修改: drivers/video/fbdev/core/fbcon_ccw.c 修改: drivers/video/fbdev/core/fbcon_cw.c 修改: drivers/video/fbdev/core/fbcon_rotate.c 修改: drivers/video/fbdev/core/fbcon_ud.c 修改: include/linux/fb.h 修改: include/linux/font.h 修改: lib/fonts/Kconfig 修改: lib/fonts/Makefile 新文件: lib/fonts/font_cjk_16x16.c 新文件: lib/fonts/font_cjk_16x16.h 新文件: lib/fonts/font_cjk_32x32.c 新文件: lib/fonts/font_cjk_32x32.h 修改: lib/fonts/fonts.c

2.5 日志默认屏幕而非串口

diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-pc.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s-pc.dtsi index e49f5fa..ff182b1 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-pc.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s-pc.dtsi @@ -10,7 +10,7 @@ / { chosen: chosen { - bootargs = "earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 irqchip.gicv3_pseudo_nmi=0 root=PARTLABEL=rootfs rootfstype=ext4 ro rootwait overlayroot=device:dev=PARTLABEL=userdata,fstype=ext4,mkfs=1 coherent_pool=1m systemd.gpt_auto=0 cgroup_enable=memory swapaccount=1 net.ifnames=0"; + bootargs = "earlycon=uart8250,mmio32,0xfeb50000 irqchip.gicv3_pseudo_nmi=0 root=PARTLABEL=rootfs rootfstype=ext4 ro rootwait overlayroot=device:dev=PARTLABEL=userdata,fstype=ext4,mkfs=1 coherent_pool=1m systemd.gpt_auto=0 cgroup_enable=memory swapaccount=1 net.ifnames=0"; };

2.6 支持exfat

diff --git a/arch/arm64/configs/linux.config b/arch/arm64/configs/linux.config index 9a23fc2..6c569cd 100644 --- a/arch/arm64/configs/linux.config +++ b/arch/arm64/configs/linux.config @@ -6,6 +6,7 @@ CONFIG_TOUCHSCREEN_HX83102=y CONFIG_CAN=y CONFIG_CANFD_ROCKCHIP=y CONFIG_OVERLAY_FS=y +CONFIG_EXFAT_FS=y CONFIG_SND_SOC_FIREFLY_MULTICODECS=y

2.7 fit支持ramdisk打包

diff --git a/boot.its b/boot.its index 755005c..885bfd1 100644 --- a/boot.its +++ b/boot.its @@ -45,6 +45,21 @@ algo = "sha256"; }; }; + + ramdisk { + description = "Compressed Initramfs"; + data = /incbin/("ramdisk"); + type = "ramdisk"; + arch = "arm64"; + os = "linux"; + compression = "none"; + load = <0x00000000>; + entry = <0x00000000>; + + hash { + algo = "sha256"; + }; + }; }; configurations { @@ -55,6 +70,7 @@ fdt = "fdt"; kernel = "kernel"; multi = "resource"; + ramdisk = "ramdisk";

三、 开机脚本

参考仓库 https://gitlab2.kylin.com/shanghai-team/ramdisk/-/tree/ramdisk-trial

开机运行如下

function start_install() { parse_cmdline prepare_iso if [ "${TRIAL}" == "true" ];then overlay_start return 0 fi while true do clear info "感谢使用麒麟系统,请选择安装模式" info "1) 试用" info "2) 安装" info "3) 调试" info "4) 重启" info "请输入:" read -s -n1 -t 30 -r option case ${option} in 1) overlay_start return 0 ;; 2) _start_install ;; 3) debug_shell ;; 4) echo b > /proc/sysrq-trigger info "你的系统不支持重启" ;; *) [ -z ${option} ] && _start_install ;; esac done }

四、适配流程

  • 获取RK3588产品版本镜像
  • 通过任意方式制作U盘启动盘
  • 将待更新机器uboot通过rkdevtool烧录为uboot_usb.img,以支持USB启动
  • 将U盘插入设备上开机--→等待机器亮屏显示绿色字符“感谢使用麒麟系统,请选择安装模式”
  • 选择1即可试用,选择2即可安装,选择3即可调试,选择4即可重启

五、研发流程

  • 获取设备uboot代码
  • 将设备树内置在dtb目录下
  • 修改CONFIG_EMBED_KERNEL_DTB_PATH变量为指定的dtb
  • 将uboot编译为uboot-usb.img,以待usb启动版uboot使用
  • 将原生loader.bin编译成idblock.img和rkxxx.bin
  • 内核内打包ramdisk.img
  • 克隆ramdisk.git仓库编译出ramdisk.img/initrd.lz/initrd.img/uInitrd
  • 将内核编译为boot.img/uImage
  • 将开机图片置于启动盘根目录logo_kernel.bmp/logo.bmp
  • 为项目编写parameter-kylin.txt
  • 打包ISO

六、 结语

至此,已经可以获得基于RK3588上使用U盘安装系统的全套方案。客户如果需要设备支持U盘安装,则只需要设备的uboot代码和kernel代码即可正常配置。下面链接版本是支持一款板卡的uboot安装ISO镜像。

https://builder.kylin.com/kybuilder/build/view/49340

编辑
2025-03-03
工作知识
0

随着git项目越来越多,每次环境部署的时候,不可能一个一个去clone仓库,这样太费事了。为了偷懒,我借鉴了android的repo机制,这样可以自行对自己的git仓库搭建一个简单的repo,之后使用这个repo就能方便的管理多个git仓库了。

一:环境搭建

1.1 克隆repo

git clone https://gerrit.googlesource.com/git-repo

如果上网不方便,可以克隆本地仓库。

git clone http://10.3.4.182/arm-embedded/git-repo.git -b kylin-server-130-31

由于repo会把repo url默认到 https://gerrit.googlesource.com/git-repo。这里修改成130.31的本地git地址。也可修改成gitlab地址。

如:REPO_URL = 'http://10.3.4.182/arm-embedded/git-repo.git'

[root@localhost git-repo]# git diff diff --git a/repo b/repo index 3a51cce..c084b65 100755 --- a/repo +++ b/repo @@ -142,8 +142,6 @@ if __name__ == '__main__': REPO_URL = os.environ.get('REPO_URL', None) if not REPO_URL: REPO_URL = 'https://gerrit.googlesource.com/git-repo' +# kylin local repo url + REPO_URL = 'root@172.25.130.31:/root/work2/git/git-repo.git' REPO_REV = os.environ.get('REPO_REV') if not REPO_REV: REPO_REV = 'stable'

如果不做上述修改,则需要在repo init的时候,加入 --repo-url http://10.3.4.182/arm-embedded/git-repo.git

将git仓库里的repo更新到/usr/bin下。如下:

[ ! -f /usr/bin/repo ] && ln -s `pwd`/git-repo/repo /usr/bin/repo

输入 repo version 如果正常返回,则repo不存在问题

1.2 创建测试的git仓库

cd /root/work2/git git init --bare test1.git git init --bare test2.git git init --bare test3.git git clone /root/work2/git/test1.git git clone /root/work2/git/test2.git git clone /root/work2/git/test3.git cd test1 && echo "hello world" > Readme && git add . && git commit -m "first commit" && git push && cd - cd test2 && echo "hello world" > Readme && git add . && git commit -m "first commit" && git push && cd - cd test3 && echo "hello world" > Readme && git add . && git commit -m "first commit" && git push && cd -

如果本身就有仓库,就无需创建,直接用即可。这里仅做测试验证

因为测试使用的服务器,为了ssh免密处理,这里需要把公钥拷过去

ssh-copy-id root@172.25.130.31

repo并不是在所有环境都能正常运行,它需要满足python到3.7及以上。

所以如果repo运行异常,则需要更新系统python到3.7及以上

1.3 repo设置

git init --bare manifests.git git clone /root/work2/git/manifests.git vim manifests.git <?xml version="1.0" encoding="UTF-8"?> <manifest> <remote fetch="ssh://root@172.25.130.31/root/work2/git" name="origin"/> <default remote="origin" revision="master" sync-j="4"/> <project name="test1" path="test1"/> <project name="test2" path="test2"/> <project name="test3" path="test3"/> </manifest> git add . && git commit -m "first commit" && git push

这里通过编写xml,设置远端的git仓库为root@172.25.130.31/root/work2/git, 远端的分支名字为origin,默认的remote为origin,版本分支为master,一共三个工程,分别为test1 test2 test3,并在本地创建同样的目录名字。

这样即可编写一个最简单的manifests.xml。后续可以使用这个xml来管理整个git仓库

二: repo使用

2.1 拉取repo仓库

repo init -u root@172.25.130.31:/root/work2/git/manifests.git repo sync

此时可以看到三个仓库被正常的拉取。

[root@localhost 3]# ls test1 test2 test3

2.2 设置快照

在repo不断更新的过程中,可以生成快照,实际上就是生成一个xml文件,它保持了当时的repo状态,可供其他人使用。

这个类似于git tag。当需要整体出一个版本的时候,可以直接生成快照。

例如:

repo manifest -r -o tag_v1.0.xml <?xml version="1.0" encoding="UTF-8"?> <manifest> <remote fetch="ssh://root@172.25.130.31/root/work2/git" name="origin"/> <default remote="origin" revision="master" sync-j="4"/> <project dest-branch="master" name="test1" revision="831a146ccf46fe060c74f15354e9d68c3e0afbcb" upstream="master"/> <project dest-branch="master" name="test2" revision="829b53cce4538d5ae5919bc42bea7254bc1dcd9b" upstream="master"/> <project dest-branch="master" name="test3" revision="750f8d272da0b85a7bb7c42427fe9f0c72d416be" upstream="master"/> </manifest>

这里就比较显而易见了,快照保持了每个分支的分支信息,commit信息和上游信息

2.3 基本使用

查看分支状态

repo status

为某个分支创建新的branch

repo start bug_1.2 test1

上面这个命令等同于

git checkout -b bug_1.4

查看分支信息

repo branch

查看所有分支的diff

repo diff

其他就不列举了。因为repo实际就是git命令的封装,有时间可能在仓库里面直接运行git会更加方便。

三:manifests仓库的xml解释

3.1 标准头

默认的xml头

<?xml version="1.0" encoding="UTF-8"?>

顶层manifest元素

<manifest> </manifest>

3.2 remote 元素

<remote />
name 这里的name是git remote的那个远程名字,默认是origin。如果是git的remote是其他的,这里填写对应即可 fetch 这里是git的地址,如果是ssh可以是:ssh://root@172.25.130.31/root/work2/git 如果是http或者git,就是github/gitlab上的地址 pushurl 这里是push的地址,当指定该属性的时候,这个值会和<project>标签中的name属性拼成完整的push url路径,这样当我们使用git push命令的时候,就会使用该url。如果不指定该属性,则pushurl和fetch一样。 revision 这里是默认的git branch名字。可以是master或者其他

3.3 default 元素

<default />
remote 这里是remote设置的名字,代表repo拉的分支默认使用哪个remote上的 revision 当一个<project>不指定revision的时候使用该值 dest-branch 默认创建的本地分支,如果不指定,就是revision默认创建的分支 sync-j repo sync的并行数目 sync-c 如果设置为true,则只同步指定的分支(revision 属性指定),而不是所有的ref内容 sync-s 如果设置为true,则会同步git的子项目 sync-tags

3.4 project 元素

<project />
name: 这里是工程名也就是哪个仓库 ${remote_fetch}/${project_name}.git path: 显式声明的存放文件路径 remote: 这里指定的远程分支名字 revision: 这里指定需要获取的git提交点,可以是master, refs/heads/master, tag或者SHA-1值。如果不设置的话,默认下载当前project,当前分支上的最新代码。 upstream: 在哪个git分支可以找到一个SHA1。用于同步revision锁定的manifest(-c 模式)。该模式可以避免同步整个ref空间。

3.5 include 元素

xml可以直接include子xml。通过如下方式

<manifest> <include name="default.xml" /> </manifest>

3.6 linkfile和copyfile属性

<project name="test1" path="test1"> <linkfile dest="Readme" src="Readme"/> <copyfile dest="Readme.md1" src="Readme.md"/> </project>

在project下面设置可以设置软连接和拷贝。实际上等效于如下

ln -s test1/Readme Readme cp test1/Readme.md Readme.md1

四:参考链接:

https://gerrit-googlesource.proxy.ustclug.org/git-repo

https://gerrit.googlesource.com/git-repo/+/master/docs/manifest-format.md