近日,OpenCloudOS RISC-V SIG 成功适配  Milk-V Megrez 开发板,适配成果已经同步到 OpenCloudOS 镜像官网,欢迎下载体验[1]。

此次工作由中国科学院软件研究所 PLCT 实验室实习生 Malachite 完成,mentor 是来自苦芽科技的工程师孙敏,委托培养社区为 OpenCloudOS 社区,这是「甲辰计划开源实习生联合招聘培养」的一部分。

关于 OpenCloudOS Stream

OpenCloudOS Stream (后续简称 OC)  是由  OpenCloudOS  社区与合作伙伴共同打造的自主可控上游发行版。经内核与用户态组件全栈调优,它能提供先进的高性能基础环境,能够有效应对类似 CentOS  断供的风险,而且加入了一系列自有内核补丁与功能,如允许非特权进程绑定低端口、进程与挂载点保护、系统调用与套接字审计等,非常适合云原生、容器化应用。

Milk-V Megrez 开发板介绍

Milk-V Megrez  是一款专注 AI 及虚拟化的 RISC-V 开发板,其搭载一个 4 核 SiFive P550 CPU (ESWIN EIC7700X SoC,支持 64 位 RV64GBCH 标准指令集),主频最高可达  1.8 GHz,最大支持 32 GB LPDDR5 内存(6400MT/s)。Megrez 指令集支持 H 扩展,能够有效提升  RISC-V 原生虚拟化性能。

图片

Megrez 开发板

适配成果展示

当前 OpenCloudOS Stream  已经能够初步适配  Milk-V Megrez,完成内核编译和开机启动流程,能够启动 Wayland 最小桌面环境,且已验证多种功能,最新源码位于[2],欢迎围观。

图片

浏览器支持|Wayland 环境(可选)|H 扩展

图片

功能适配进展(对标 RockOS)

只需三步,快速体验适配成果

  • 获取发行版/烧录镜像

    从 OpenCloudOS 官网下载镜像文件[1],解压缩后写入到 EMMC 或者 SD 卡之类的存储器,推荐用 Etcher 之类工具执行烧镜像的操作。

    wget https://mirrors.opencloudos.tech/opencloudos-stream/releases/23/images/riscv64/sdcard/ocs_developer_sdcard-megrez.img.xz
    
  • 上电开机

    注意:上电之前,开发板需要通过串口线(USB Type-C 数据线)与开发机连接!

    将 OpenCloudOS Stream 镜像写入磁盘(SD 卡)并通电开机时,u-boot 可能并不能自动识别到 grub 引导器,因此需要打开一个 uboot 命令行。为了告诉 u-boot 从哪里启动,您可以根据安装的存储介质选择以下语句,粘贴到 u-boot 命令行中执行。

    #SATA硬盘
    setenv bootcmd 'load sata 0:1 0x84000000 EFI/BOOT/BOOTRISCV64.EFI; bootefi 0x84000000'; saveenv
    #eMMC模块
    setenv bootcmd 'load mmc 0:1 0x84000000 EFI/BOOT/BOOTRISCV64.EFI; bootefi 0x84000000'; saveenv
    #SD卡
    setenv bootcmd 'load mmc 1:1 0x84000000 EFI/BOOT/BOOTRISCV64.EFI; bootefi 0x84000000'; saveenv
    
    

    当 saveenv 成功之后,就可以尝试执行 boot 命令,引导 OpenCloudOS Stream 了。

    图片

  • 联网

    通过命令行可以成功连接到指定的 WiFi。

    ##扫描附近的WiFi列表
    nmcli device wifi scan
    ##连接热点
    nmcli device wifi connect '想要连接的WiFi的SSID' password '密码'
    
    

这是一条分割线,以下适配流程由 Malachite 讲述

我手上的 Megrez 开发板简介

图片

Megrez 组装图

我打算这样适配

在我适配 OpenCloudOS 之前,已有两个操作系统适配了 Megrez,分别是 Fedora 和 RockOS,针对 OpenCloudOS 内核的适配思路是这样的:从目前与它兼容性最好的 RockOS  内核移植驱动和设备树等硬件相关源码。以下是我在移植内核过程中遇到的难点和解决方案。

看起来要合并内核补丁

OpenCloudOS  内核版本基线为  6.6.68  且含有不少定制补丁,而  RockOS  则没有发布这个版本,两者之间存在“合并冲突”。为防止驱动未完整移植和 OpenCloudOS 自有补丁被覆盖等不期望的情况,我需要阅读源码,总结与 EIC7700 SoC  有关的文件特征,然后进行批量搜索。例如将两个内核源码树都放在$HOME 目录(~)下,然后使用如下命令识别出与 SoC  有关的驱动代码:

for i in $(grep -ril -e 'eswin' -e 'eic7' -e 'win2030' -e 'sifive' .)
> do
>   if cmp -s $i ~/OpenCloudOS-Kernel/$i; then
>       echo $i
>   fi
> done

然后使用  meld  工具对比两个文件夹,将  OpenCloudOS  不存在的文件复制过去,对于两边都存在的文件,则进行具体的行级比对和取舍,以“解决合并冲突”。

难以琢磨的 UAPI  头文件

移植过程中,有几个  UAPI  头文件无法通过  HDRTEST,然而它们在  RockOS  能通过编译且在  OpenCloudOS  的失败原因为“存在 C++风格注释,不符合  ANSI C  标准”、“size_t 没有定义” 等。经判断,它们是编译驱动必须的文件,且这些错误一般不会在用户使用时发生,故将这些头文件加入了  usr/include/Makefile  的  no-header-test 清单。

从 6.6.73 到 6.6.88

适配工作开展初期,我错误地认为为了移植方便应选取版本号尽量接近、差异补丁数量最少的  6.6.73  版本  RockOS  内核进行移植。然而移植到能够开机的程度之后却发现有几个无论如何修改  config  或提供用户空间固件都无法解决的开机  dmesg  报错,USB_DWC3  和  mailbox  等硬件无法正常初始化。

经检验发现是  6.6.73  版本的  RockOS  设备树尚不完善所致,而更换设备树需要同步改动驱动,故只能改用当前最新  6.6.88  版本  RockOS  内核源码全部推倒重来。初步完成代码移植之后,内核编译  config  也需要进行调整。

NUMA 配置选项让我困惑

起初,我尝试从  OpenCloudOS Stream 23 QEMU  中的/proc/config.gz  导出,然后

make oldconfig

编译之后,发现许多与  EIC7700  相关的选项并不会默认开启,导致直接编译出的内核无法正常开机,对照  RockOS  的  eic7700_defconfig 调整起来非常麻烦。相反,OpenCloudOS  相关的选项在  menuconfig 中相当清晰。因此,最终我选择从 eic7700_defconfig 模板创建  config,再在  menuconfig 中对特定选项(如  NUMA、Tencent Kernel  特性等)进行调整。这样一来,编译出能开机、能正常驱动各种硬件的内核理应变得容易许多。

但是,若遵照  RockOS  的配置不设置  NUMA,则  mm/memcontrol.c  的编译无法通过;开启  NUMA,则开机初期  dmesg  会产生两条关于  NUMA  的警告:

[    0.000000] NUMA:Warning: invalid memblk node 8[mem 0x0000000480000000-0x000000087ffccfff]
[    0.000000] NUMA:Faking a node at [mem 0x0000000080000000-0x00000010003fffff]

这是内核从设备树获取到的内存布局信息与它期望的  NUMA  节点配置不匹配,fallback  到  UMA  所致。由于 RockOS  没有设置  NUMA,且  EIC7700X  只有四个核心,推测为“设备树不支持”。为了完整适配该硬件,NUMA  选项理应处于关闭状态,但编译又无法通过。

挖掘到一个 OCS 内核补丁 BUG

阅读源码排查后,我发现这是一个由 OpenCloudOS  内核自有补丁[3]引入的问题,在启用  MEMCG  但关闭  NUMA  时  mm/memcontrol.c  中部分函数会调用部分该条件下不会被编译的函数导致编译错误,该故障也可在  x86_64  复现。

设法修复该缺陷之后,启用  MEMCG  但关闭  NUMA  时编译能够通过。当前修复该缺陷的补丁已经推送到 OpenCloudOS-Kernel  最新开发分支[4]。

成功的喜悦

至此,该内核就已经能够正常通过编译、通过  dracut 根据内核模块生成  initramfs、开机引导进入系统。开机后会出现 win2030-noc 报错(另外两个适配了  Megrez 的发行版也存在同样的问题)、推测可能是 RTC 没有插电池导致的无法写入时钟的寄存器的报错和 DSP、NPU 等缺少用户空间闭源固件导致无法加载驱动报错以外基本没有其他错误,可以开始功能验证。

顺道验证 KVM 功能

  • 读取 CPU 信息

    图片

  • 编译内核中的 KVM 测试项并运行通过

    图片

  • 利用 KVM 和大内存,成功以 snapshot 方式启动 ubuntu 镜像

从  RockOS 软件源下载 u-boot-qemu 的 deb 并提取 S-mode ELF 格式的 uboot(OpenCloudOS Stream 软件源内不存在)之后,执行

qemu-system-riscv64 --enable-kvm -M virt \
-cpu host -m 16384-smp 4-nographic \
-kernel ./uboot.elf \
-drive file=ubuntu-25.04-preinstalled-server-riscv64.img,format=raw,if=virtio,snapshot=on

图片

这种方式不会将变更写入到磁盘镜像,将来可推广至  OpenCloudOS 的软件包测试开发流程中,在简化部署、尽量不损失性能的前提下带来不被构建依赖污染的干净环境。

迫不及待验证 Tencent Kernel 特性

  • 允许非特权用户绑定特定低编号端口

    图片

  • 加载 aegis 内核模块,读取 execve 系统调用审计报告

    图片

  • 利用同上内核模块读取 socket 审计报告

    图片

再唠叨几句

目前为  Milk-V Megrez 平台适配的  OpenCloudOS Stream  内核已经完成了包括  KVM  在内大部分硬件功能上的适配,也保留了  Enhanced MM、Tencent Kernel  等自有功能特性。目前的  OpenCloudOS Stream  镜像不算完整,缺乏可用的桌面环境,我们还需要经过整合用户空间无需额外配置的  rootfs、固件和驱动、完善适合普通用户的启动流程等环节。

路漫漫其修远兮

  • 功能适配方向:ESWIN SDK、NPU 内核驱动、用户空间 GPU 驱动与图形栈等多处问题与需要 ESWIN 官方支持与合作的地方
  • 性能优化方向:结合  OpenCloudOS 的内核特性以及云原生业务场景做针对性的优化工作。
关于实习生 Malachite
“喜欢玩 Linux 开发板,擅长 shell 与 VBA”

参考链接

[1] https://mirrors.opencloudos.tech/opencloudos-stream/releases/23/images/riscv64/sdcard/ocs_developer_sdcard-megrez.img.xz

[2] https://gitee.com/malachite/OpenCloudOS-Kernel/tree/p550-dev/

[3] https://gitee.com/OpenCloudOS/OpenCloudOS-Kernel/commit/321e018e4b3fa18cbcc73a97f9cba48a8f911624

[4] https://gitee.com/OpenCloudOS/OpenCloudOS-Kernel/pulls/438