背景

上一章节完成了 lk1st 的移植,为Linux主线内核的引导和ARM64 Linux 的启动提供了基础。此时 fastboot 正常工作,但是 fastboot boot 无法启动下一步。

本章即将进入到Linux主线内核的真正启动阶段,

获取Linux内核源码

获取内核源码

1
2
3
cd ~/y927
git clone https://github.com/msm8916-mainline/linux.git -b wip/msm8916/7.0 --depth 1
cd linux

第0阶段:零外设验证内核引导启动

目标

最极简的验证:不需要串口、不需要 USB、不需要看屏幕任何输出。只需要一个秒表。

原理:

1
内核启动 → 挂载 initramfs → 执行 /init → sleep 15 秒 → exit 触发 panic → kernel 重启

观察设备行为:

  • 刷入 boot.img 后重启
  • 屏幕亮起(lk1st splash) → 停 ~15 秒 → 重启 → 再次亮起(lk1st)
  • 如果这个循环发生了,内核就启动了。

不需要任何调试外设,不需要 DTB 写的正确——只需要内核能成功解压、初始化基础 SoC、挂载 initramfs 并运行 init 进程。

最简的骨架 DTS

只要一个骨架设备树,告诉内核这是 MSM8916 平台即可:

1
2
3
4
5
6
7
8
9
10
11
// SPDX-License-Identifier: GPL-2.0-only

/dts-v1/;

#include "msm8916-pm8916.dtsi"

/ {
model = "vivo Y927 (PD1410V)";
compatible = "vivo,y927", "qcom,msm8916";
chassis-type = "handset";
};

只有 10 行!msm8916-pm8916.dtsi 已经把 SoC 时钟、中断控制器、timer、regulator 等基础全部定义好了。内核只需要 compatible = "qcom,msm8916" 就能初始化 MSM8916 平台。

不写任何外设节点——没有串口、没有 USB、没有 eMMC。内核启动后唯一能做的事就是从 initramfs 执行 /init

编辑Makefile linux/arch/arm64/boot/dts/qcom/Makefile

1
2
3
# dtb-$(CONFIG_ARCH_QCOM)	+= msm8916-vivo-y21l.dtb
dtb-$(CONFIG_ARCH_QCOM) += msm8916-vivo-y927.dtb
# ...

制作一个最简 initramfs

Linux 内核完成自举后,会启动用户态第一个进程,也就是 PID 为 1 的 /init 程序。

BusyBox 是遵循 GPL 协议的开源轻量工具集,被称作嵌入式 Linux 的 “瑞士军刀”。它将数百款 Linux 常用命令与工具整合为单个可执行文件,通过软链接和调用名区分不同功能,具备体积小巧、可灵活裁剪的特点,广泛应用于嵌入式设备、微型 Linux 系统及容器场景。

常规完整 Linux 系统中,/init 一般为 SysV init 或 systemd;而搭建极简 initramfs 无需这类重型初始化程序。BusyBox 内置了精简版 init,运行逻辑与 SysV init 相近,完全可以胜任最简系统的初始化工作。

编译 BusyBox

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
cd ~/y927

# 下载源码
wget https://busybox.net/downloads/busybox-1.38.0.tar.bz2
tar xf busybox-1.38.0.tar.bz2
rm busybox-1.38.0.tar.bz2
cd busybox-1.38.0

# 配置交叉编译器
export CROSS_COMPILE=aarch64-linux-gnu-
export ARCH=arm64

# 生成默认配置
make defconfig
# 改为静态链接(不依赖任何 .so)
sed -i 's/# CONFIG_STATIC is not set/CONFIG_STATIC=y/' .config

# 确认这些必要命令是否已开启
grep -E "CONFIG_(SLEEP|ASH|MOUNT|REBOOT|INIT)=" .config
# CONFIG_SLEEP=y
# CONFIG_REBOOT=y
# CONFIG_INIT=y
# CONFIG_MOUNT=y
# CONFIG_ASH=y

# 关闭TC支持,这个命令不需要,但是不关闭会导致编译失败
sed -i 's/CONFIG_TC=y/CONFIG_TC=n/' .config

# 开始多线程编译
make -j$(nproc)

file busybox
# busybox: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=8d5f61a63654db70668b4b0419e6f781e2e5acc3, for GNU/Linux 3.7.0, stripped

制作 initramfs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mkdir -p ~/y927/initramfs/simple-0
cd ~/y927/initramfs/simple-0
mkdir -p bin && cd bin
cp ~/y927/busybox-1.38.0/busybox .
ln -s busybox init
ln -s busybox ash
ln -s busybox sh
ln -s busybox sleep

# 写入 init 脚本
cd ~/y927/initramfs/simple-0
cat > init << 'EOF'
#!/bin/sh
/bin/sleep 15
EOF
chmod +x init

# 打包
find . | cpio -o -H newc | gzip > ../initramfs-simple-0.cpio.gz

编译 Linux 内核

1
2
# 安装编译依赖包
sudo apt install flex bison bc rsync libssl-dev libelf-dev -y
1
2
3
4
5
cd ~/y927/linux

# 用默认 defconfig —— 不需要改任何配置
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- msm8916_defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) Image.gz dtbs

构建 boot.img 文件

可以使用mkbootimg工具制作boot.img文件。mkbootimg是专门打包 Android Boot Image 的工具,这个工具之前在lk2nd仓库中存在。

boot.img的格式如下:

字段 大小 描述
ANDROID! header 2048B header_verrsion=0 固定 2KB
kernel - Image.gz + DTB
ramdisk - cpio.gz
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 拼接内核 + DTB
cat arch/arm64/boot/Image.gz \
arch/arm64/boot/dts/qcom/msm8916-vivo-y927.dtb \
> vmlinuz-dtb

~/y927/lk2nd/lk2nd/scripts/mkbootimg \
--kernel vmlinuz-dtb \
--ramdisk $HOME/y927/initramfs/initramfs-simple-0.cpio.gz \
--cmdline "console=ttyMSM0,115200 panic=1" \
--base 0x80000000 \
--kernel_offset 0x00008000 \
--ramdisk_offset 0x01000000 \
--second_offset 0x00f00000 \
--tags_offset 0x00000100 \
--pagesize 2048 \
--header_version 0 \
--output boot-phase0.img

刷入 boot.img 文件

1
2
fastboot flash boot boot-phase0.img
fastboot reboot

重启后,手机会陷入 15s 的重启循环中,说明最简 Linux 内核启动成功!

第1阶段:启动 USB 模拟串口进入 Linux 进行操作

目标

阶段0验证了内核能够正常引导成功,但是看不到任何输出。本阶段的目标是通过 USB Gadget Serial 实现串口交互式输入输出,可以通过 USB 线在 PC 端与手机 Linux 系统进行交互。

设备树 DTS

编写以下 DTS 初始化 USB:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// SPDX-License-Identifier: GPL-2.0-only

/dts-v1/;

#include "msm8916-pm8916.dtsi"
#include <dt-bindings/gpio/gpio.h>

/ {
model = "vivo Y927 (PD1410V)";
compatible = "vivo,y927", "qcom,msm8916";
chassis-type = "handset";

usb_id: usb-id {
compatible = "linux,extcon-usb-gpio";
id-gpios = <&tlmm 110 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&usb_id_default>;
};
};

&usb {
dr_mode = "peripheral";
extcon = <&usb_id>, <&usb_id>;
status = "okay";
};

&usb_hs_phy {
extcon = <&usb_id>;
status = "okay";
};

&tlmm {
usb_id_default: usb-id-default {
pins = "gpio110";
function = "gpio";
drive-strength = <8>;
bias-pull-up;
};
};

制作 initramfs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
BUSYBOX_BINARY=~/y927/busybox-1.38.0/busybox
cd ~/y927/initramfs
rm -rf phase-1
mkdir -p phase-1 && cd phase-1

# 创建 initramfs 目录结构
mkdir -p bin dev proc sys

# 复制 BusyBox 到 initramfs
cp $BUSYBOX_BINARY bin/

# 创建基础命令的软链接
cd bin
for cmd in sh mount ls cat sleep exec echo; do
ln -sf busybox $cmd
done
cd ..

# 创建 /init 脚本
cat > init << 'EOF'
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sys /sys
mount -t devtmpfs dev /dev
echo "===== Phase 1: kernel booted! ====="
echo "Serial: $(cat /proc/cmdline)"
sleep 5
exec sh </dev/ttyGS0 >/dev/ttyGS0 2>&1
EOF
chmod +x init

# 打包
find . | cpio -o -H newc | gzip > ../initramfs-phase-1.cpio.gz

内核编译配置

1
2
cd ~/y927/linux
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- msm8916_defconfig

msm8916_defconfig 只需额外启用 USB gadget serial 相关选项:

1
2
3
./scripts/config --enable CONFIG_USB_G_SERIAL
./scripts/config --enable CONFIG_U_SERIAL_CONSOLE
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- olddefconfig

olddefconfig 会自动解析新选项的 Kconfig 依赖链(USB_CHIPIDEA、USB_GADGET、USB_CONFIGFS 等),避免手动改 .config 导致的依赖缺失。

编译 Linux 内核

1
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) Image.gz dtbs

产物:

  • arch/arm64/boot/Image.gz — 压缩内核
  • arch/arm64/boot/dts/qcom/msm8916-vivo-y927.dtb — 设备树 blob

构建 boot.img 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cd ~/y927/linux

# 拼接内核 + DTB
cat arch/arm64/boot/Image.gz \
arch/arm64/boot/dts/qcom/msm8916-vivo-y927.dtb \
> vmlinuz-dtb

~/y927/lk2nd/lk2nd/scripts/mkbootimg \
--kernel vmlinuz-dtb \
--ramdisk $HOME/y927/initramfs/initramfs-phase-1.cpio.gz \
--cmdline "console=ttyGS0,115200 panic=1" \
--base 0x80000000 \
--kernel_offset 0x00008000 \
--ramdisk_offset 0x01000000 \
--second_offset 0x00f00000 \
--tags_offset 0x00000100 \
--pagesize 2048 \
--header_version 0 \
--output boot-phase1.img

cmdline 只启用 USB 串口,不碰屏幕(无 console=tty0、无 lk2nd.pass-simplefb)。

刷入 boot.img 文件

1
2
fastboot flash boot boot-phase1.img
fastboot reboot

USB 串口交互式输入输出测试

PC 端安装 picocom:

1
sudo apt install -y picocom

连接(需 root 权限访问 /dev/ttyACM0):

1
sudo picocom -b 115200 /dev/ttyACM0

启动成功后应看到:

1
/ # _

此时可以使用busybox中打包的所有命令

串口终端 退出 picocom:Ctrl+A 松手 → Ctrl+X