RaspberryPi4B编译含KVM的64位内核

运行环境

准备工作

  1. 要编译64位的内核必须要使用64位工具链和系统,而Raspbian是32位的所以只能交叉编译

  2. 安装编译所需工具和交叉编译链

1
sudo apt install git bc bison flex libssl-dev make libncurses-dev gcc-aarch64-linux-gnu
  1. 如需使用Linaro的交叉编译链请按这步操作,主要是可以自定义编译器版本,否则跳过
1
2
3
4
5
# 下载并配置交叉编译链到系统环境
wget https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/aarch64-linux-gnu/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz
xz -d gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz
tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar
export PATH=$PATH:/home/leux/rpi4/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin
  1. 当前树莓派官方默认的内核是4.19版本,如需其他内核版本请自选
1
2
3
4
# export all_proxy="127.0.0.1:7890"	# 设置代理,没有跳过
mkdir /home/leux/rpi4
cd /home/leux/rpi4
git clone -b rpi-4.19.y --depth=1 https://github.com/raspberrypi/linux.git

开始编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
cd linux/
make mrproper # 只在首次编译前执行,删除所有的编译生成文件、内核配置文件和各种备份文件
make clean # 删除编译后生成文件
make dep # 配置依赖关系

# 在运行64位内核的Raspbian系统内获取官方的内核编译配置文件
# 需要先加载configs模块才能获取到/proc/config.gz这个内核编译配置文件
# 然后复制config.gz到内核源码目录下生成并使用Raspbian的内核编译配置文件
echo "arm_64bit=1" > /boot/config.txt && sudo reboot
sudo modprobe configs
zcat /proc/config.gz > ~/raspi.config

# 当然也可以直接指定内核配置文件为默认的配置,再根据开启虚拟的参数打开KVM支持
# make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig menuconfig


# 在内核配置界面根据后面开启虚拟的参数打开KVM的支持
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig

# 默认编译内核及模块和设备树,可以指定参数 Image modules dtbs 来单独编译需要的内容
# 完成后会在目录arch/arm64/boot中生成Image内核文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j12

开启虚拟

  1. 在内核的字符配置界面根据下面参数来开启内核对KVM的支持

  2. 下面参数中符号 > 代表上下级菜单,符号 - 表示同级菜单下

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
KVM		:Virtualization > Kernel-based Virtual Machine support
vhost-net :Virtualization > Host kernel accelerator for virtio net
vhost跨端支持 :Virtualization > Cross-endian support for vhost

PCI直通 :Bus support > PCI Stub driver
virtio :Device Drivers > Virtio drivers > PCI driver for virtio devices
virtio-balloon :Device Drivers > Virtio drivers > Virtio balloon driver
virtio-input :Device Drivers > Virtio drivers > Virtio input driver
VIRTIO_MMIO :Device Drivers > Virtio drivers > Platform bus driver for memory mapped virtio devices # 允许内存映射到virtio设备
virtio-blk :Device Drivers > Block devices > Virtio block driver
virtio-blk-scsi :Device Drivers > Block devices > Virtio block driver > SCSI passthrough request for the Virtio block driver
virtio-scsi :Device Drivers > SCSI device support > SCSI low-level driver > virtio scsi support
virtio-net :Device Drivers > Network device support > Network core driver support > Virtio network driver
virtio-console :Device Drivers > Character devices > Virtio console
virtio-hw-random:Device Drivers > Character devices > Hardware Random Number Generator Core support > Virtio Random Number Generator support
virtio-drm-gpu :Device Drivers > Graphics support > Direct Rendering Manager - Virtio GPU driver
arm_iommu :Device Drivers > IOMMU Hardware Support > ARM Ltd. System MMU (SMMU) Support - ARM Ltd. System MMU Version 3 (SMMUv3) Support
vfio :Device Drivers > IOMMU Hardware Support - VFIO Non-Privileged Userspace driver framework
vfio-pci :Device Drivers > VFIO Non-Privileged Userspace driver framework > VFIO support for PCI devices
vfio-mdev :Device Drivers > VFIO Non-Privileged Userspace driver framework > Mediated device driver framework
VIRT_DRIVERS :Device Drivers > Virtualization driver

virtio-crypto-device :Cryptographic API > Hardware crypto devices > Virtio crypto driver
virtio-9p :Networking support > Plan 9 Resource Sharing Support > 9P Virtio Transport
NET_9P_RDMA :Device Drivers > InfiniBand support
:Networking support > Plan 9 Resource Sharing Support > 9P Virtio Transport > 9P RDMA Transport

VIRTIO_VSOCKETS :Networking support > Networking options > Virtual Socket protocol > virtio transport for Virtual Sockets
VHOST_VSOCK :Virtualization > vhost virtio-vsock driver
CAIF :Networking support > CAIF support
CAIF_VIRTIO :Device Drivers > Network device support > CAIF virtio transport driver
  1. 上面参数打开后会包含但不限于以下功能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1. virtio		# 标准虚拟磁盘
2. virtio-scsi # 虚拟scsi磁盘
3. virtio-blk # 磁盘直通
4. virtio-blk-scsi # scsi模式磁盘直通
5. virtio-net # 虚拟网卡
6. virtio-balloon # 内存气泡
7. virtio-hw-random # 虚拟随机数硬件
8. virtio-console # 虚拟终端
9. virtio-input # 虚拟输入设备
10. virtio-crypto-device# 虚拟加密设备
12. virtio-drm-gpu # 虚拟显卡
13. virtio-9p # 目录共享
14. vfio # 设备直通
15. vhost # 主机数据交互
16. vhost-net # 主机网络数据交互
...

拷贝文件

  1. 安装内核模块和头文件到临时文件夹/home/leux/rpi4/kernel/下
1
2
3
sudo su		# 切换到root用户再安装模块,否则模块权限将混乱
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- INSTALL_MOD_PATH=/home/leux/rpi4/kernel/ modules_install
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- INSTALL_HDR_PATH=/home/leux/rpi4/kernel/usr headers_install
  1. 保存内核及设备树到临时文件夹
1
2
3
4
5
cd /home/leux/rpi4/kernel/ && mkdir -p boot/overlays
cp ../linux/arch/arm64/boot/Image boot/kernel-kvm.img
cp ../linux/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dtb boot/
cp ../linux/arch/arm64/boot/dts/overlays/*.dtbo boot/overlays
find ./usr/include/ -name *.install* | xargs rm -f # 清理头文件中的无用文件
  1. 将保存的内核及模块复制到官方Raspbian系统的对应位置
1
2
3
4
# arm64的内核不提供自解压功能,让Bootloader解压除外,所以必须是未压缩的映像文件
mv kernel/lib/modules/4.19.122-v8+ /lib/modules/
mv kernel/boot/kernel-kvm.img /boot/
echo "kernel=kernel-kvm.img" >> /boot/config.txt
  1. 在官方Raspbian系统上通过chroot使用KVM
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
# 因为Raspbian的Rootfs是32位的,而KVM的基本条件就是64位QEMU
# 所以Raspbian是无法使用KVM的,不过可以通过chroot一个64位的Rootfs来使用KVM
# 或者使用交叉编译的64位静态QEMU也可以在Raspbian上使用KVM,但这里只讲chroot方式

# 获取Debian Buster的64位Rootfs
sudo su && apt install debootstrap
debootstrap --arch=arm64 buster /opt/debian/ http://ftp.cn.debian.org/debian

# 挂载设备并进入64位的Rootfs
mount -o bind /dev /opt/debian/dev
mount -o bind /proc /opt/debian/proc
mount -o bind /sys /opt/debian/sys
chroot /opt/debian/ /bin/bash -l

# 在64位的Rootfs环境中安装QEMU来使用KVM
apt update && apt install qemu-system-arm qemu-efi-aarch64 seabios vgabios

# 创建一个20G的虚拟硬盘文件
qemu-img create -f qcow2 system.qcow2 20G

# 以启动Win10 ARM64为例,2004.iso是安装光盘,virtio-win.iso是驱动光盘
# 通过VNC客户端连接 [树莓派IP]:5900 来显示界面
qemu-system-aarch64 \
-M virt-2.12 -smp 4 -m 2G -cpu host -enable-kvm \
-bios /usr/share/qemu-efi-aarch64/QEMU_EFI.fd \
-device ramfb -device qemu-xhci,id=xhci \
-device usb-kbd -device usb-tablet -k en-us \
-device virtio-blk,drive=system \
-drive if=none,id=system,cache=unsafe,file=system.qcow2 \
-device usb-storage,drive=install \
-drive if=none,id=install,format=raw,media=cdrom,file=2004.iso \
-device usb-storage,drive=drivers \
-drive if=none,id=drivers,media=cdrom,file=virtio-win.iso \
-device virtio-net,disable-legacy=on,netdev=net0 \
-netdev user,id=net0,hostfwd=tcp::3389-:3389 -vnc :0

引导程序

  1. 官方的bootloader并不开源,但是提供了二进制文件下载

  2. 以前的树莓派都是使用GPU加载bootcode.bin和fixup.dat来对内存等进行初始化并使用start.elf启动kernel.img,而4B因为有了EEPROM所以不再加载bootcode.bin而是直接加载start4.elf和fixup4.dat来启动kernel.img

  3. 想要树莓派4B启动Linux,boot分区下必须拥有以下七个文件:

1
2
3
fixup4.dat start4.elf			:适用于4B的引导程序
kernel8.img config.txt cmdline.txt :内核及启动配置文件
bcm2711-rpi-4-b.dtb overlays/ :设备树文件
  1. 官方对引导程序的说明
1
2
3
稳定版:fixup.dat   和start.elf		# 后面带4的为树莓派4B的版本
测试版:fixup_x.dat 和start_x.elf # 这种版本拥有比正常版本更多的功能
精简版:fixup_cd.dat和start_cd.elf # 用于显存只有16MB时且会缺少部分CPU功能
  1. 配置支持功能文件config.txt
1
2
3
4
5
arm_64bit=1			# 启用64位内核
kernel=kernel8.img # 指定启动内核
dtoverlay=disable-bt # 关闭蓝牙功能
dtoverlay=vc4-fkms-v3d # 开启硬件加速
dtparam=audio=on # 开启音频输出
  1. 设置内核引导文件cmdline.txt
1
2
3
4
5
6
7
8
console=serial0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 fsck.repair=yes rootwait quiet 

console=serial0,115200 # 串口使用哪个设备,以及传输速率
console=tty1 # 控制台输出使用tty1设备
root=/dev/mmcblk0p2 # 将内存卡第二分区设置为根分区
rootfstype=ext4 # 挂载根分区格式为ext4类型
fsck.repair=yes # 启动时自动检查修复文件系统错误
rootwait # 等待内核识别根分区设备后再挂载
  1. 将上面七个文件保存的文件或文件夹复制到SD卡的boot分区就可以用来引导了

  2. 要完整的启动Linux系统还需要rootfs,有rootfs的话还需要把前面生成的内核模块复制到对应路径下