为荔枝派Zero制作Linux系统镜像
最近购入了一块LicheePi Zero
开发板,使用全志V3s
作为主控芯片。本文介绍如何从零开始为荔枝派制作一个可以启动的Linux
系统镜像。
配置编译环境
本文所有编译工作均基于Ubuntu 20.04LTS
系统。
安装交叉编译器
V3s
为ARM架构,为了能够生成ARM架构的代码,首先需要一个交叉编译器,其中最常用的为linaro
公司推出的arm-linux-gnueabihf
交叉编译器。可以前往Linaro Toolchain页面下载x86_64
版本的交叉编译器。截至本文完成之时,编译器最新版本为7.5.0
,下文也将以这个版本为例进行说明。
首先,下载交叉编译器,并安装到/opt
目录下。
cd ~/Download
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
sudo tar xf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C /opt
sudo mv gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf arm-linux-gnueabihf-gcc #重命名文件夹为arm-linux-gnueabihf-gcc
下载并安装后,还需要将编译器所在路径加入到PATH中,以便make
编译时调用。
echo "PATH=\$PATH:/opt/arm-linux-gnueabihf-gcc/bin" >> ~/.bashrc
source ~/.bashrc
编译系统镜像
编译U-boot
首先,从GitHub拉下荔枝派官方提供的修改过的U-boot
源码。
cd ~/code/licheepi-zero
git clone https://github.com/Lichee-Pi/u-boot -b v3s-current
在编译之前,还需要安装一些依赖。
sudo apt install u-boot-tools python3-distutils python3-dev swig build-essential libncurses5-dev git
接下来,进入代码文件夹,根据使用的屏幕分辨率,直接编译即可。
cd u-boot
# make ARCH=arm LicheePi_Zero_480x272LCD_defconfig
# make ARCH=arm LicheePi_Zero_800x480LCD_defconfig #根据你使用的屏幕分辨率进行选择
make ARCH=arm LicheePi_Zero_defconfig #没有屏幕
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
编译生成的U-boot
在u-boot
的根目录下,名为u-boot-sunxi-with-spl.bin
。
编译Linux内核
从GitHub拉下荔枝派官方提供的Linux
源码。这里我使用了5.2
版本的Linux
内核。
cd ~/code/licheepi-zero
git clone https://github.com/Lichee-Pi/linux.git -b zero-5.2.y
此仓库中的源码已经适配了音频、有线网卡等的驱动,并已修改完毕dtb。拉取完成后,在编译前还需要安装一些依赖。
sudo apt install flex bison libssl-dev
之后,进入源码文件夹,使用官方提供的sunxi_defconfig
配置文件,对代码进行配置。
cd linux
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- sunxi_defconfig
默认的配置文件中,并没有开启有线网卡的驱动。如果需要使用有线网卡,还需要在menuconfig
中开启。首先执行
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make menuconfig
在菜单中,选择下面菜单项中的网卡驱动支持即可。
Device Drivers —>
Network device support —>
Ethernet driver support —>
[*] STMicroelectronics devices
<*> STMicroelectronics 10/100/1000/EQOS Ethernet driver
<*> STMMAC Platform bus support
< > Support for snps,dwc-qos-ethernet.txt DT binding.
<*> Generic driver for DWMAC
<*> Allwinner GMAC support
<*> Allwinner sun8i GMAC support
此仓库中源码内的dts
文件中只启用了UART0
这一个串口作为调试串口使用。若需要使用所有3个串口,还需要对dts
文件进行修改。首先对\arch\arm\boot\dts\sun8i-v3s.dtsi
进行修改,加入对串口引脚的定义。
uart0_pins_a: uart0@0 { pins = "PB8", "PB9"; function = "uart0";bias-pull-up; };
uart1_pins_a: uart1@0 { pins = "PE21", "PE22"; function = "uart1";bias-pull-up; };
uart2_pins_a: uart2@0 { pins = "PB0", "PB1"; function = "uart2";bias-pull-up; };
然后修改\arch\arm\boot\dts\sun8i-v3s-licheepi-zero.dts
,启动这3个串口。
&uart0 { pinctrl-0 = <&uart0_pins_a>; pinctrl-names = "default";status = "okay"; };
&uart1 { pinctrl-0 = <&uart1_pins_a>; pinctrl-names = "default";status = "okay"; };
&uart2 { pinctrl-0 = <&uart2_pins_a>; pinctrl-names = "default";status = "okay"; };
配置完成后,对源码进行编译。
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
得到内核镜像zImage
和设备树sun8i-v3s-licheepi-zero.dtb
,分别位于arch/arm/boot/zImage
和arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dtb
。
编译rootfs
从BuildRoot网站上下载Buildroot源码并解压。这里我以Buildroot 2020.02
为例。
cd ~/Downloads
wget https://buildroot.org/downloads/buildroot-2020.02.8.tar.gz
tar xf buildroot-2020.02.8.tar.gz -C ~/code/licheepi-zero
cd ~/code/licheepi-zero/buildroot-2020.02.8
mv buildroot-2020.02.8 buildroot-2020.02 # 重命名
make menuconfig
在编译之前,需要安装BuildRoot的依赖项。
sudo apt install python texinfo unzip
首先,对CPU架构Target options
进行配置。
Target Architecture (ARM (little endian)) --->
Target Binary Format (ELF) --->
Target Architecture Variant (cortex-A7) --->
Target ABI (EABIhf) --->
Floating point strategy (VFPv4-D16)
ARM instruction set (ARM)
然后,对工具链Toolchain
进行配置。
Toolchain type (External toolchain) --->
*** Toolchain External Options ***
Toolchain (Custom toolchain) --->
Toolchain origin (Pre-installed toolchain)
(/opt/arm-linux-gnueabihf-gcc) Toolchain path
(arm-linux-gnueabihf) Toolchain prefix
External toolchain gcc version (7.x) --->
External toolchain kernel headers series (4.10.x) --->
External toolchain C library (glibc/eglibc) --->
[*] Toolchain has SSP support?
[*] Toolchain has SSP strong support?
[*] Toolchain has RPC support?
[*] Toolchain has C++ support?
[ ] Toolchain has D support?
[ ] Toolchain has Fortran support?
[ ] Toolchain has OpenMP support?
[ ] Copy gdb server to the Target
*** Host GDB Options ***
[ ] Build cross gdb for the host
*** Toolchain Generic Options ***
[ ] Copy gconv libraries
() Extra toolchain libraries to be copied to target
[*] Enable MMU support
() Target Optimizations
() Target linker options
[ ] Register toolchain within Eclipse Buildroot plug-in
接下来可以在Target packages
中选择自己需要的模块。这里以minicom
与python3
为例。
Target packages --->
Hardware handling --->
[*] minicom
Interpreter languages and scripting --->
[*] python3
python3 module format to install (.py sources and .pyc compiled) --->
core python3 modules --->
External python modules --->
[*] python-pip
[*] python-serial
配置完成后保存退出,编译。
make
输出文件在output/images/rootfs.tar
。
烧录镜像
直接烧录到TF卡
假设TF卡的设备是/dev/sdb
。接下来使用GParted
对TF卡进行格式化与分区。
首先删除TF卡上的所有分区。
然后,新建存放系统镜像设备树与启动脚本的boot
分区。右键点击未分配的空间,选择新建,新建一个前部有1M
空闲空间,大小为32M
的FAT32
分区,命名为boot
。
接下来,选择剩余的所有空间,创建一个ext4
格式的rootfs
分区(即为点击新建后默认的选项)。
新建完分区的整块磁盘看起来像这样。
请再三确认,之前格式化的时候创建boot
分区时,分区前预留了1M
的空间。确认无误后,点击绿色的对勾提交更改,TF卡格式化就完毕了。提交更改之后,为了防止系统无法立即挂载新的分区,建议将读卡器拔出后重新插入。
接下来便是写入各个部分的代码了。首先使用下列命令写入u-boot
。写入之前请再次确认读卡器的设备号为sdb
,以防损坏硬盘内的数据。
cd ~/code/licheepi-zero/u-boot
sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/sdb bs=1024 seek=8
接下来,将licheepi-zero/linux/arch/arm/boot/zImage
和licheepi-zero/linux/arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dtb
两个文件复制到boot
分区中。
然后,将licheepi-zero/buildroot-2020.02/output/images/rootfs.tar
解压到rootfs
分区中。这必须要使用命令行操作,请确认rootfs
挂载的路径,通常来说为/media/用户名/rootfs
。
4102a4fc-7b8a-4f3b-9c68-31c0ef9ffac4
。下面指令中具体的卷标或uuid需要根据实际情况进行设置。sudo tar xf ~/code/licheepi-zero/buildroot-2020.02/output/images/rootfs.tar -C /media/wangyz/rootfs
sudo sync # 将更改写入卡中
最后,编写启动脚本。启动脚本用于指定u-boot
的启动行为,包括内核与设备树的文件名、应该将哪个分区挂载为rootfs
等。在~/code/licheepi-zero/
目录下,新建boot.cmd
,将下面内容写入。
setenv bootargs console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 earlyprintk rw
load mmc 0:1 0x41000000 zImage
load mmc 0:1 0x41800000 sun8i-v3s-licheepi-zero.dtb
bootz 0x41000000 - 0x41800000
然后,编译启动脚本。
mkimage -C none -A arm -T script -d boot.cmd boot.scr
得到的boot.scr
即为启动脚本,将其复制到boot
分区,即完成了可启动TF卡的制作。
制作烧录镜像
上述操作比较麻烦,且不方便将可启动的内存卡镜像发给他人。因此,可以使用脚本将上述镜像打包为一个烧录文件,直接烧录进TF卡。此处参考了坑网帖子分享全志主线u-boot/linux 打包 TF/SD/SDNAND 镜像脚本并做出了一些修改。只需要将脚本文件内容新建为pack.sh
,放入~/code/licheepi-zero
路径下,使用sudo
运行即可。运行前请确认脚本中的各个路径是否正确。
#!/bin/bash
_UBOOT_SIZE=1
###第一个分区(FAT)大小,单位MiB##
_P1_SIZE=32
###TF卡镜像文件名称###
_IMG_FILE='a20_generic_sdcard.bin'
###TF卡镜像文件大小, 单位MiB###
_IMG_SIZE=512
###存放各个文件的路径
temp_root_dir=$PWD
_UBOOT_FILE="${temp_root_dir}/u-boot/u-boot-sunxi-with-spl.bin"
_KERNEL_IMAGE_FILE="${temp_root_dir}/linux/arch/arm/boot/zImage"
_DTB_FILE="${temp_root_dir}/u-boot/arch/arm/dts/sun8i-v3s-licheepi-zero.dts"
_ROOTFS_TGZ_FILE="${temp_root_dir}/buildroot-2020.02/output/images/rootfs.tar"
###初始化镜像文件###
dd if=/dev/zero of=$_IMG_FILE bs=1M count=$_IMG_SIZE
###判断镜像文件是否初始化成功###
if [ $? -ne 0 ]
then
echo "getting error in creating dd img!"
exit
fi
###获取一个循环设备###
_LOOP_DEV=$(sudo losetup -f)
##再次判断此设备是否存在###
if [ -z $_LOOP_DEV ]
then
echo "can not find a loop device!"
exit
fi
###把镜像文件和循环设备关联###
sudo losetup $_LOOP_DEV $_IMG_FILE
if [ $? -ne 0 ]
then
echo "dd img --> $_LOOP_DEV error!"
sudo losetup -d $_LOOP_DEV >/dev/null 2>&1 && exit
fi
echo "--->creating partitions for tf image ..."
###分区###
cat <<EOT |sudo sfdisk ${_IMG_FILE}
${_UBOOT_SIZE}M,${_P1_SIZE}M,c
,,L
EOT
###格式化###
sleep 2
sudo partx -u $_LOOP_DEV
sudo mkfs.vfat ${_LOOP_DEV}p1 ||exit
sudo mkfs.ext4 ${_LOOP_DEV}p2 ||exit
if [ $? -ne 0 ]
then
echo "error in creating partitions"
sudo losetup -d $_LOOP_DEV >/dev/null 2>&1 && exit
fi
###u-boot写到TF卡8K偏移处###
echo "--->writing u-boot-sunxi-with-spl to $_LOOP_DEV"
sudo dd if=$_UBOOT_FILE of=$_LOOP_DEV bs=1024 seek=8
if [ $? -ne 0 ]
then
echo "writing u-boot error!"
sudo losetup -d $_LOOP_DEV >/dev/null 2>&1 && exit
fi
###新建 p1,p2 目录,并挂载TF卡两个分区###
sudo sync
mkdir -p ${temp_root_dir}/output/p1 >/dev/null 2>&1
mkdir -p ${temp_root_dir}/output/p2 > /dev/null 2>&1
sudo mount ${_LOOP_DEV}p1 ${temp_root_dir}/output/p1
sudo mount ${_LOOP_DEV}p2 ${temp_root_dir}/output/p2
echo "--->copy boot and rootfs files..."
sudo rm -rf ${temp_root_dir}/output/p1/* && sudo rm -rf ${temp_root_dir}/output/p2/*
###复制zImage, dtb, boot.scr 等文件到第一分区 ###
sudo cp ${_KERNEL_IMAGE_FILE} ${temp_root_dir}/output/p1/zImage &&\
sudo cp ${_DTB_FILE} ${temp_root_dir}/output/p1/ &&\
sudo mkimage -C none -A arm -T script -d ${temp_root_dir}/boot.cmd ${temp_root_dir}/output/p1/boot.scr
echo "--->p1 done~"
###解压 rootfs.tgz 到第二分区###
sudo tar xf ${_ROOTFS_TGZ_FILE} -C ${temp_root_dir}/output/p2/ &&\
echo "--->p2 done~"
###同步, 等待, 卸载, 退出###
sudo sync
sleep 2
sudo umount ${temp_root_dir}/output/p1 ${temp_root_dir}/output/p2 && sudo losetup -d $_LOOP_DEV
if [ $? -ne 0 ]
then
echo "umount or losetup -d error!!"
exit
fi
sudo rm -rf output