红米 Note 4X (mido) + Ubuntu 26.04
群友一直说要拿旧手机代替开发板装智能小车上。直接给不用的红米 Note 4X 刷上给他好了。
准备
红米 Note 4X 原版系统基本上符合一般 Android 手机的引导方式,内核什么的和 initramfs 封装一下放 boot 分区里,由前一级引导程序启动。如果不动前一级引导程序,给 Ubuntu 的内核和 initramfs 按照 Android Boot Image 的格式放到 boot 分区里按理说就行了,不过在屏幕面板型号识别之类的细节上多少会出问题。我倒是不知道下游内核是怎么做的,据说方法比较扭曲。postmarketOS 的方法是在 boot 分区的位置装个 lk2nd,由 lk2nd 再拉起内核,在拉起内核之前它会从前一级引导程序那里获取面板型号,再改 Device Tree 里对应节点加入正确的面板 compatible 信息,内核这块就不用考虑面板识别的事了。
内核和 initramfs 还要有地方放。lk2nd 支持的位置挺灵活的,我喜欢放 boot 分区里 lk2nd 后面,从 boot 分区首偏移 512K 的位置,和 postmarketOS 的做法一样。在那里开个 MBR 分区表,新建个 ext2 分区,给内核、initramfs 和 Device Tree 放里面,写好配置文件就能用了。
userdata 和 system 算是红米 Note 4X 这样的手机上容量前两大的分区,直接给 Ubuntu 系统分区放 userdata 里就挺好的。想给 system 利用上也行,但是两个分区组合当然要麻烦一点,我觉得一般也不会缺那几 G 空间。
总结下来实际要做的不外乎就是把 userdata 分区改成 Ubuntu 的系统分区,给 boot 分区换成 lk2nd+MBR+ext2 的结构。
编译内核
在座各位都在本科计组课上编译过内核。在这种手机上装现代发行版当然要自己编译内核,免得有什么需要的内核功能没开,或者什么驱动走的是 Android HAL 之类的情况。红米 Note 4X 这么知名的机器,这块的作业显然已经有人帮我们写过了。postmarketOS 在机型适配这块做挺干净的,内核 config 和 early modules 都放源码库里了,抄起来像呼吸一样简单。
postmarketOS 的内核打成了软件包,从 APKBUILD 里直接找到内核源码用的仓库和版本,再给 config 拉下来重命名为内核源码下 .config 文件。
我手里这台换过液晶面板,现在装的是杂牌面板,兼容原版 nt35532 驱动,直接用的话屏幕会发蓝没法正常工作,要在面板初始化之前 sleep 个 50 毫秒:
diff --git a/linux-6.12.0-r0/drivers/gpu/drm/panel/msm8953-generated/panel-xiaomi-nt35532.c b/linux-6.12.0-r0/drivers/gpu/drm/panel/msm8953-generated/panel-xiaomi-nt35532.c
index fb2fa8b..398ef8c 100644
--- a/linux-6.12.0-r0/drivers/gpu/drm/panel/msm8953-generated/panel-xiaomi-nt35532.c
+++ b/linux-6.12.0-r0/drivers/gpu/drm/panel/msm8953-generated/panel-xiaomi-nt35532.c
@@ -102,6 +102,8 @@ static int nt35532_prepare(struct drm_panel *panel)
}
nt35532_reset(ctx);
+ msleep(50);
+ pr_info("Wait for 50ms after reset");
ret = nt35532_on(ctx);
if (ret < 0) {
.config 装好,准备开始编译。我电脑是 x86 的,所以需要再调一下交叉构建的设置。CROSS_COMPILE 的字段根据你发行版提供的交叉编译工具链名字来定,如果你发行版没提供的话大概要自己打一个编译器,不过在座各位应该也都会。我比较喜欢再给提示符前面加个前缀,免得忘记自己动了什么。
export PS1="(cross compile) ${PS1}"
export ARCH=arm64
export CROSS_COMPILE=aarch64-suse-linux-
make olddefconfig 一下,直接 make -j<并行任务数>,之后等着就行了。
编译好之后内核会出现在 arch/arm64/boot/Image.gz 下。
配置 rootfs
先装一个通用的 Ubuntu 系统,再往里面塞与平台相关的文件。
直接拉 Ubuntu 给的 rootfs 镜像,解压。我当时测试的时候 Ubuntu 还没出 26.04 正式版,所以是从 24.04 升上去的。
为什么不用 debootstrap …… debootstrap 操作看起来挺简单的,实际不比 rpm 系或者 Arch 系发行版省心多少,有时候本地脚本版本不对就没法生成想要的版本的 rootfs,而且一堆 Perl 脚本看都看不懂,出了 bug 都没法自己修。
如果你电脑配了 qemu-binfmt,应该能直接 chroot 进配置好的 rootfs,内核会自己找正确的 qemu 模拟执行里面的程序。反正也就改改配置文件生成个 initramfs,对性能没要求。
先改改基本配置文件给 /etc/fstab 加一行:
/dev/mmcblk0p49 / ext4 defaults 0 0
之后设置个 root 密码,开始装平台相关的内容。为什么不一步到位给桌面环境装上……镜像太大了不好刷,而且有些 3D 加速需要的固件现在拿不到,硬开 Display Manager 的话可能会给输入设备锁死,很麻烦。
平台相关的文件差不多就是内核模块和固件,要在运行时加载当然就要放 rootfs 里了。回到之前内核源码的根目录下装模块,make modules_install INSTALL_MOD_PATH=<目录>,比如你直接装 rootfs 里的话那应该是 make modules_install INSTALL_MOD_PATH=<rootfs所在目录>/usr/lib/modules/。
之后装下部分固件和关键的包,再准备生成 initramfs:
apt install initramfs-tools linux-firmware systemd network-manager
固件好多是在外围设备上跑的程序,也有一些是配置文件或者无线模块的校准文件啥的。linux-firmware 源里只有一些公开的固件,还有些要在手机上别的分区里提取,不过只用 linux-firmware 的话对于点亮屏幕进 Linux console 已经足够了。
装完之后改 /etc/initramfs-tools,选一下镜像的压缩模式,我比较喜欢 zstd。模块加载项为 list,具体列表内容 postmarketOS 也给了,直接抄。话说 lk2nd 要求 initramfs 大小不能超过16M……奇怪的规定。如果打出来的镜像太大,加大一下压缩比吧。
做个 ext4 镜像,差不多就是 fallocate 个大点的文件,在上面创建文件系统。给 rootfs 里的文件复制到里面备用。
配置引导程序
按照前面提到的 boot 分区结构,装 lk2nd 和启动需要的文件。
既然要做一个前 512K 是 lk2nd,后面放 MBR 分区表和 ext2 分区的结构,听起来是要手动打一个这样的镜像,还是有点麻烦的。不过实际操作的时候可以先给 lk2nd 刷上,lk2nd 有 fastboot 协议,用电脑 fastboot flash boot 的时候会自动将镜像写到偏移 512K 的位置,这样直接给 lk2nd 提供一个 MBR+ext2 的镜像就行了,刷完自然会形成 lk2nd+MBR+ext2 的结构。
显然这个操作只是 lk2nd 的 fastboot 功能在实现的时候特殊处理了一下刷 boot 分区时的行为,正常用原厂 fastboot 还有在内核里读 boot 分区时都是不存在的,所以在 Linux 下升级 initramfs 什么的都还是要老老实实挂个偏移 512K 的回环设备。
在座各位都会用 fastboot。先进原厂 fastboot,给 lk2nd 刷到 boot 分区。完事之后可以进 lk2nd 的界面确认一下能不能用,再准备后面的镜像:
parted <文件>
进 parted 交互界面之后:
mktable msdos
mkpart primary ext2 2048s 100%
set 1 esp on
set 1 boot on
完事之后挂载一下 ext2 分区,不想手算分区偏移量的话挂个回环设备并开下分区表探测,网上都有文档。
把需要的文件塞到 ext2 分区里:内核构建目录下 arch/arm64/boot/dts/qcom/msm8953-xiaomi-mido.dts 放 dtbs/ 下面;再给 Image.gz 和生成的 initramfs 放方便的位置上,我这里直接放根目录。
写一下 /extlinux/extlinux.conf,内核/initramfs/device tree 的路径按照自己实际文件名来改。
default Ubuntu
label Ubuntu
linux /Image.gz
initrd /initrd.img-6.17.7-msm8953
fdtdir /dtbs
append root=/dev/mmcblk0p49 init=/usr/lib/systemd/systemd
进 lk2nd,用 fastboot 给这个镜像刷到 boot 分区里。
安装系统分区
前面配好的 Ubuntu 镜像,刷 userdata 分区里。
开机,安装固件
linux-firmware 里没有的固件在几个单独的分区里。postmarketOS 那边有个小脚本会挂载这些分区方便拿固件,不过我们直接给固件复制到系统分区里也一样。
/dev/mmcblk0p1:
wcnss.* venus.* adsp.* -> /lib/firmware/
/dev/mmcblk0p26:
WCNSS_qcom_wlan_nv.bin -> /lib/firmware/wlan/prima/
/dev/mmcblk0p48:
a506_zap.* -> /lib/firmware/qcom/msm8953/xiaomi/mido/
a530_pm4.fw -> /lib/firmware/qcom/
完了之后可能要重新 modprobe 一下 qcom_wcnss_pil。
在 Ubuntu 下访问 boot 分区
如果你要在 Ubuntu 下更新 initramfs 的话……还是得挂载 boot 分区。
别忘了我们 boot 分区里面还有个分区表,还偏移了 512K。手算 ext2 分区的位置太麻烦了,直接指定偏移量创建 loop 设备:
losetup -f -P -o 524288 /dev/mmcblk0p21
之后 lsblk 应该就能看到探测到的分区,直接挂载就能用了。
相机
红米 Note 4X 的相机是走 I2C 总线控制,用 CSI 传输内容。postmarketOS Wiki 上列了支持的传感器,拿 i2cdetect 扫一下……根据地址判断我们的机器用的传感器没有支持,算了。
使用
装个喜欢的桌面,随便搞个 GNOME 啥的,反正这台机器也不是我用。送群友之前可以先拿 Box86 跑个 Wine 玩玩,
先前还跑了个《植物大战僵尸95》,可惜 wined3d 在 freedreno 上多少有点问题,改 .drirc 假装成别的制造商也没有什么效果。骁龙 625 的 freedreno 不支持 turnip(Vulkan 部分的驱动),遂作罢。


