尝试在RISC-V上运行Waydroid
我头脑发热为了那小破比赛淘了块电子垃圾,比赛结束后绞尽脑汁想榨干他的剩余价值。
众所周知,MilkV Jupiter是一个8核16G内存的RISC-V PC,既然内存这么大,那么就从开虚拟机榨干内存开始吧!但是这破CPU并没有实现H扩展,KVM是没可能了,那么就玩玩容器,欸,Android我玩了老久了,就玩个Android容器Waydroid吧~
折腾结果: Waydroid勉强在MilkV Jupiter(SpaceMiT X60, Ubuntu 23.10)上跑了起来,使用llvmpipe调用CPU渲染,很卡,连玩具的水平都没有...
但是这玩意的好处是通用性,理论上可以在合适的RISC-V机器上的使用Wayland的桌面环境中运行。
Current Status: WIP
On MilkV Jupiter(SpaceMiT X60, Ubuntu 23.10), it can boot into home launcher, but extremely slow. Ideally it can work on every RISC-V Linux using desktop environment with Wayland support.
效果图
可以在项目首页查看
食用方法
安装软件包
对于Ubuntu 22.04及24.04,可以直接添加PPA源:
sudo add-apt-repository ppa:tanyuliang2/waydroid-riscv
sudo apt update
对于Ubuntu 23.10(MilkV Jupiter上我测试运行的版本),可以在Github这里下载deb安装。
下载镜像并初始化waydroid
镜像system.img
和vendor.img
可以从Github这里下载,并拷贝到/usr/share/waydroid-extra/images
目录下。
使用如下命令初始化之后,在桌面上启动waydroid就能食用啦。
sudo waydroid init -f
MilkV Jupiter上额外的设置
gralloc不能从GPU的/dev/card0
分配显存,但是可以从SoC的display engine /dev/card1
获取。
首先修改/var/lib/waydroid/waydroid.cfg
,在[properties]
节添加以下设置,然后sudo waydroid upgrade -o
更新
[properties]
gralloc.gbm.device = /dev/dri/card1
然后修改/var/lib/waydroid/lxc/waydroid/config_nodes
在/dev/card0那一行后加一行:
lxc.mount.entry = /dev/dri/card0 dev/dri/card0 none bind,create=file,optional 0 0
lxc.mount.entry = /dev/dri/card1 dev/dri/card1 none bind,create=file,optional 0 0
过程记录
了解Android on RISC-V支持
直至写文章这一刻,似乎没有找到Google官方明确说某个版本支持RISC-V的,而根据google的这个仓库的构建指南来看,应该是Android 14开始提供官方支持。这个说法和T-Head他们的Android on RISC-V的路线图似乎是一致的。
RISC-V社区方面,有这个RISC-V Android仓库,这个有PLCT Lab的参与,最终选定构建android 10的waydroid也是使用了他们仓库的patches。
还有一个arv仓库,他们做的事情可能是在jh7100的板子上跑起来Android,根据这里的描述来看,Android 12是能够启动的,但也同样缺乏GPU驱动的支持。
而T-Head那边,通过licheepi 4a官网找到了这个项目是T-Head官方的Android支持,看上去应该是Android 14并且有GPU驱动的支持。
尝试构建redroid 14
在尝试构建waydroid镜像前,我先是尝试了redroid,因为redroid有android 14的支持,但是结果连ART虚拟机都开不动...构建过程中也遇到很多麻烦,看起来好像对RISC-V的支持还不太足够?也可能是我漏了一些patch或者版本太老(redroid 14是android14.0.0_r2),缺了一些代码,由于我对这块知之甚少,于是很快放弃了直接构建redroid 14的想法。
报错日志:
08-27 03:39:20.638 131 131 F libc : Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 in tid 131 (main), pid 131 (main)08-27 03:39:20.677 198 198 W crash_dump64: failed to read selinux label: Bad file descriptor
08-27 03:39:20.695 198 198 I crash_dump64: obtaining output fd from tombstoned, type: kDebuggerdTombstoneProto 08-27 03:39:20.696 43 43 I tombstoned: received crash request for pid 131 08-27 03:39:20.697 198 198 I crash_dump64: performing dump of process 131 (target tid = 131) 08-27 03:39:21.050 198 198 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 08-27 03:39:21.050 198 198 F DEBUG : Build fingerprint: 'redroid/redroid_riscv64/redroid_riscv64:14/UP1A.231005.007.A1/eng.root.20240811.134956:userdebug/test-keys'
08-27 03:39:21.050 198 198 F DEBUG : Revision: '0'
08-27 03:39:21.050 198 198 F DEBUG : ABI: 'riscv64'
08-27 03:39:21.050 198 198 F DEBUG : Timestamp: 2024-08-27 03:39:20.715055098+0000
08-27 03:39:21.050 198 198 F DEBUG : Process uptime: 2s
08-27 03:39:21.050 198 198 F DEBUG : Cmdline: zygote64
08-27 03:39:21.050 198 198 F DEBUG : pid: 131, tid: 131, name: main >>> zygote64 <<<
08-27 03:39:21.050 198 198 F DEBUG : uid: 0
08-27 03:39:21.050 198 198 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0000000000000000
08-27 03:39:21.050 198 198 F DEBUG : Cause: null pointer dereference
08-27 03:39:21.051 198 198 F DEBUG : gp 000000385d2ea000 tp 000000385ccf9cc8 t0 00000000705c1408 t1 000000362b5e2c6c
08-27 03:39:21.051 198 198 F DEBUG : t2 0000000000000000 t3 000000384fab22a0 t4 000000362b5d025c t5 000000362b5d0304
08-27 03:39:21.051 198 198 F DEBUG : t6 000000362b5d03c4 s0 000000379e3ca380 s1 000000382e3ca560 s2 0000000000000000
08-27 03:39:21.051 198 198 F DEBUG : s3 0000003fe226dfb0 s4 0000000000000000 s5 0000003fe226e050 s6 0000003fe226dfb0
08-27 03:39:21.051 198 198 F DEBUG : s7 000000362b9fb498 s8 0000000000000001 s9 0000003fe226dfc8 s10 0000000000000000
08-27 03:39:21.051 198 198 F DEBUG : s11 0000003fe226dfec a0 0000003fe226dfd8 a1 0000000000000000 a2 0000000000000000
08-27 03:39:21.051 198 198 F DEBUG : a3 000000379e3cadd0 a4 000000379e3cab7f a5 0000003fe226df58 a6 0000003fe226e050
08-27 03:39:21.051 198 198 F DEBUG : a7 0000000000000000
08-27 03:39:21.051 198 198 F DEBUG : pc 000000362b2d8e1a ra 000000362b5cfd3a sp 0000003fe226dd10
08-27 03:39:21.051 198 198 F DEBUG : 45 total frames
08-27 03:39:21.051 198 198 F DEBUG : backtrace:
08-27 03:39:21.051 198 198 F DEBUG : #00 pc 00000000004b9e1a /apex/com.android.art/lib64/libart.so (void art::interpreter::ExecuteSwitchImplCpp<false>(art::interpreter::SwitchImplContext*)+296) (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.051 198 198 F DEBUG : #01 pc 00000000007b0d36 /apex/com.android.art/lib64/libart.so (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.051 198 198 F DEBUG : #02 pc 00000000004ab226 /apex/com.android.art/lib64/libart.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool, bool) (.__uniq.112435418011751916792819755956732575238)+476) (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.051 198 198 F DEBUG : #03 pc 000000000079db9e /apex/com.android.art/lib64/libart.so (artQuickToInterpreterBridge+1108) (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.051 198 198 F DEBUG : #04 pc 00000000007b1632 /apex/com.android.art/lib64/libart.so (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.051 198 198 F DEBUG : #05 pc 00000000007b148e /apex/com.android.art/lib64/libart.so (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.051 198 198 F DEBUG : #06 pc 000000000035d90a /apex/com.android.art/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+394) (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.051 198 198 F DEBUG : #07 pc 00000000004b27ac /apex/com.android.art/lib64/libart.so (bool art::interpreter::DoCall<false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, bool, art::JValue*)+2004) (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.051 198 198 F DEBUG : #08 pc 00000000004bd1a6 /apex/com.android.art/lib64/libart.so (void art::interpreter::ExecuteSwitchImplCpp<false>(art::interpreter::SwitchImplContext*)+13492) (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.051 198 198 F DEBUG : #09 pc 00000000007b0d36 /apex/com.android.art/lib64/libart.so (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.052 198 198 F DEBUG : #10 pc 00000000004ab226 /apex/com.android.art/lib64/libart.so (art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool, bool) (.__uniq.112435418011751916792819755956732575238)+476) (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.052 198 198 F DEBUG : #11 pc 000000000079db9e /apex/com.android.art/lib64/libart.so (artQuickToInterpreterBridge+1108) (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.052 198 198 F DEBUG : #12 pc 00000000007b1632 /apex/com.android.art/lib64/libart.so (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.052 198 198 F DEBUG : #13 pc 00000000007b148e /apex/com.android.art/lib64/libart.so (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.052 198 198 F DEBUG : #14 pc 000000000035d90a /apex/com.android.art/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+394) (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.052 198 198 F DEBUG : #15 pc 0000000000387e26 /apex/com.android.art/lib64/libart.so (art::ClassLinker::InitializeClass(art::Thread*, art::Handle<art::mirror::Class>, bool, bool)+2622) (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.052 198 198 F DEBUG : #16 pc 000000000036c8e6 /apex/com.android.art/lib64/libart.so (art::ClassLinker::EnsureInitialized(art::Thread*, art::Handle<art::mirror::Class>, bool, bool)+132) (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.052 198 198 F DEBUG : #17 pc 00000000007b39c4 /apex/com.android.art/lib64/libart.so (NterpGetStaticField+412) (BuildId: 13579e0aad0c837425ffd1e426e48095)
08-27 03:39:21.052 198 198 F DEBUG : #18 pc 00000000004c186e /apex/com.android.art/lib64/libart.so (void art::interpreter::ExecuteSwitchImplCpp<false>(art::interpreter::SwitchImplContext*)+31612) (BuildId: 13579e0aad0c837425ffd1e426e48095)
了解LLVM的JIT支持
redroid和waydroid最开始的编译我都使用swiftshader作为渲染后端,但是我发现都不能正常启动,随后又尝试切换到mesa的llvmpipe似乎都不能工作。
开始的时候并不知道LLVM的JIT的深坑,只是看到这里,适配swiftshader on RISC-V的作者倡议升级到LLVM13,一时半会儿弄不明白为啥要升级,稀里糊涂地也跟着升,就是把waydroid的llvm_project的lineage18分支直接backport到lineage17,这个仓库的LLVM版本是17。后来在这里才了解到LLVM的JIT支持是LLVM15之后才达到开箱可用的状态。
LLVM的JIT链接器有两种实现: RuntimeDyld(MCJIT的组件)和JITLink(OrcJIT的组件),JITLink的支持已经添加到了RISC-V中,而RuntimeDyld因为官方宣布不再支持因而没有RISC-V支持的相关patch合并。
Mesa
从这篇笔记和arv仓库中jh7100设备树做过的尝试来看,SpaceMiT X60上用的这个GPU如果用开源驱动可能很大概率是驱动不了的,所以初期计划暂时不考虑使用GPU,而是使用软件方式(LLVMPIPE)进行渲染。
因为前面提到RISC-V上LLVM的JIT链接器只有JITLink,但是Mesa一直使用的是MCJIT,也就是使用RuntimeDyld作为链接器,也就是不支持RISC-V架构的。
但是在这个合并请求之后的版本中,Mesa是能够在RISC-V上运行的。搜索这个作者的相关信息,顺藤摸瓜找到了他为(版本23.2的Mesa做的patch)[https://github.com/YukariChiba/deepin-mesa/commit/8d5e7c317493d459948e8cf6f29110e39f823ca0],刚好waydroid仓库里lineageos18分支下有使用相同版本的,看起来成功概率挺大,就选择了lineageos18.1-mesa23.2这个分支加上那个patch的方案。
此外,23.2版本(或者稍微更早一些的版本)的Mesa的Android.mk有些变化,不能正常在Android 10的AOSP工程中编译,报错提示缺少某些头文件,在Mesa上搜到了这个合并请求,参考对应部分做了修改后成功编译。
打好patch的mesa仓库我放在了Github上,在这里。
系统镜像构建方法
参考Waydroid的文档克隆Lineageos项目并打上Waydroid的补丁,然后合并RISC-V Android仓库下所有项目的RISC-V补丁。
合并补丁的版本及相应的manifest.xml随后放出...关键的设备树,LLVM及Mesa库已经放在了Github上