Exynos4412 移植Linux Kernel 5.4过程记录(三)——根文件系统构建与SD卡挂载fs

Exynos4412 移植Linux Kernel 5.4过程记录(三)——根文件系统构建与SD卡挂载

Exynos4412 移植Linux Kernel 5.4过程记录系列

以上2篇博文一直移植的是Linux Kernel5.15,但是总有问题。所以,本文移植Linux Kernel5.4了。

一、嵌入式Linux文件系统基础知识

1、嵌入式Linux文件系统的构建方案

Linux文件系统不仅仅是根文件系统Cramfs,还有用户YAFFS2文件系统和临时文件系统。

  • Cramfs根文件系统:是内核启动挂载的第一个文件系统,是只读不可写的,所占存储空间小,适用于不需要经常修改的场合。
  • YAFFS2文件系统:适合Nand Flash设备,用了存储用户读写数据。
  • Ramfs/Tempfs文件系统:把所有文件都放在RAM中,读写速度快,减少对Flash的读写损坏。但是掉电后数据丢失。

2、根文件系统的两种格式

  • 镜像格式的根文件系统
  • 文件夹格式的根文件系统1

(1)镜像格式的根文件系统

镜像文件系统的扩张名为.img,它具有一定的格式,格式是内化的,跟文件名后缀是无关,而是在制作时决定的。镜像形式的根文件系统是由文件夹格式的根文件系统通过特定的工具(mke2fs)制作得到的,主要用来烧写到Nand Flash或者SD卡上,用于内核启动后挂载。

(2)文件夹格式的根文件系统

简单来说,文件夹格式的根文件系统其实就是我们开发主机下的一个文件夹而已,不同之处在于这个文件夹里面包含了我们根文件系统所需的内容(包括etc目录下的运行时配置文件、/bin等目录下的可执行程序、/lib目录下的库文件等···)。文件夹格式的根文件系统是没有格式的,它是通过我们的nfs来实现挂载的

3、文件系统目录标准

参考文献:
嵌入式 Linux根文件系统移植(二)——根文件系统简介2

二、Linux 根文件系统构建

1、下载BusyBox源代码

从官网https://busybox.net/downloads/下载BusyBox源代码。目前最新版本是busybox-1.35.0.tar.bz2。我下载的是busybox-1.34.1.tar.bz2。
新建工作目录~/rootfs/

mkdir ~/rootfs

复制BusyBox源码到该目录下,并解压

tar vxf busybox-1.34.1.tar.bz2

2、编译busybox并安装

(1)添加交叉工具链
编辑顶层目录下的Makefile文件。

cd ./busybox-1.34.1/
gedit Makefile

在Makefile中添加交叉工具链。

ARCH ?= arm
CROSS_COMPILE ?= arm-none-linux-gnueabihf-

(2)配置busybox。修改Makefile的编译选项如下:

make distclean
make defconfig
make menuconfig

按照如下文档配置menuconfig即可。

Busybox Settings--->
	Build Options--->
		[ ]Build BusyBox as a static binary(no shared libs)
		
	Busybox Library Tuning--->
		[*]vi-style line editing commands
		[*]Fancy shell prompts	
		
	Installation Options ("make install" behavior)  --->
      	  (/home/exynos4412/rootfs/rootfs) BusyBox installation prefix      // 编译生成文件的存放路径
      	  
Linux Module Utilities--->
	[ ]Simplified modutils
	[*]insmod
	[*]rmmod
	[*]lsmod		

其他都保持默认。编译BusyBox:

make -j 4

(3)安装BusyBox

make install

进入安装目录/home/exynos4412/rootfs/rootfs会发现bin linuxrc sbin usr这几个文件。说明安装成功。

3、制作根文件系统目录

进入安装目录,创建所需文件夹(根据FHS标准)

mkdir dev etc home lib proc sys tmp var
mkdir var/log

4、添加库文件

先用arm-none-linux-gnueabihf-readelf查看必需的库文件。

arm-none-linux-gnueabihf-readelf -a ./rootfs/bin/busybox | grep "program interpreter"
	[Requesting program interpreter: /lib/ld-linux-armhf.so.3]
arm-none-linux-gnueabihf-readelf -a ./rootfs/bin/busybox | grep "Shared library"
 0x00000001 (NEEDED)                     Shared library: [libm.so.6]
 0x00000001 (NEEDED)                     Shared library: [libresolv.so.2]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]

现在需要把这些链接库文件拷贝到根文件系统的/lib目录下。注意,这些是符号连接文件,你需要把它们连接的真正的库文件一起复制。

arm-none-linux-gnueabihf-gcc -print-sysroot查看交叉编译工具链所在路径。

将交叉编译工具链的/lib中的库文件复制到安装目录下的/Iib子目录,命令如下。

arm-none-linux-gnueabihf-gcc -print-sysroot
	/opt/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/../arm-none-linux-gnueabihf/libc
sudo cp /opt/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/../arm-none-linux-gnueabihf/libc/lib /home/exynos4412/rootfs/rootfs/lib -a

删除静态库和共享库文件中的符号表,命令如下。

sudo rm lib/*.a

删除调试等信息,使文件变小,命令如下。

arm-none-linux-gnueabi-strip lib/*.so

删除不需要的库,确保库的大小不超过4MB,然后用以下命令检查lib目录的大小。

du-mh lib/

5、制作hosts主机静态域名文件

在etc目录下创建hosts文件,并填充内容如下:

#by sprife for static ip
127.0.0.1 localhost
192.168.1.230 CBT4412

6、制作mdev.conf文件

把busybox源码目录下的mdev.conf文件复制到根文件系统的etc目录下。

7、创建dev目录的设备节点

mkmod命令的格式是:
mknod 设备名 设备类型(字符:c,块:b) 主设备号 从设备号

 sudo mknod -m 666 dev/tty c 5 0
 sudo mknod -m 620 dev/tty0 c 4 0
 sudo mknod -m 660 dev/ttyS0 c 4 64
 sudo mknod -m 600 dev/console c 5 1
 sudo mknod -m 666 dev/zero c 1 5
 sudo mknod -m 666 dev/urandom c 1 9 
 sudo mknod -m 666 dev/ptmx c 5 2
 sudo mknod -m 666 dev/null    c 1 3
 sudo mknod -m 666 dev/full c 1 7
 sudo mknod -m 640 dev/port c 1 4
 sudo mknod -m 666 dev/random c 1 8
 sudo mknod -m 644 dev/random c 10 235
 ln -s /proc/kcore dev/core
 chmod 1777 tmp var
 mkdir mnt/etc  mnt/jiffs2 mnt/yaffs mnt/udisk mnt/sdcard mnt/nfs
 mkdir var/lib var/lock var/log var/run var/tmp
 chmod 1777 var/tmp

mdev的用法可以参考busybox-1.9.2/docs/mdev.txt,它的两个用途:初始化/dev目录、动态更新。要在内核启动时,自动运行mdev,这需要修改etc/fstab、etc/init.d/rcS加入要自动运行的命令。

8、填充etc目录

(1)添加模板文件内容。
将Busybox源码目录中的etc目录模板内容复制到前面制作的文件系统目录的etc目录下:
这样前面制作的etc目录下就有了相应内容,默认的文件还需要手动修改才可。

cp busybox-1.34.1/examples/bootfloppy/etc/* ~/rootfs/rootfs/etc -a

(2)修改rcS文件,内容如下:

sudo gedit etc/init.d/rcS 
#!/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
runlevel=S
prevlevel=N
umask 022
export PATH runlevel prevlevel
mount -a
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
/bin/hostname -F /etc/sysconfig/HOSTNAME
ifconfig eth0 192.168.1.230

为rcS文件添加可执行权限:

chmod +x init.d/rcS

(3)修改fstab文件,内容如下:

sudo gedit etc/fstab
# /etc/fstab: static file system information.
#
# Use 'vol_id --uuid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# 	<file system> 	<mount point> 	<type> 	<options> 	<dump> 	<pass>
	proc 			/proc 			proc 	defaults 	0 		0
	sysfs 			/sys 			sysfs 	defaults 	0 		0
	tmpfs 			/var 			tmpfs 	defaults 	0 		0
	tmpfs 			/tmp 			tmpfs 	defaults 	0 		0
	tmpfs 			/dev 			tmpfs 	defaults 	0 		0
	mdev 			/dev 			ramfs 	defaults 	0 		0
/dev/mtdblock3	/mnt/yaffs		yaffs	defaults                1       1

(4)修改profile文件内容
在启动脚本etc/init.d/rcS执行完后将在终端启动一个shell。Shell启动过程中会根据文件/etc/profile配置登陆环境。

sudo gedit etc/profile
# /etc/profile: system-wide .profile file for the Bourne shells
# Ash profile
# vim: syntax=sh

# No core files by default
ulimit -S -c 0 > /dev/null 2>&1

USER="`id -un`"
LOGNAME=$USER
PS1='[\u@\h \W]\# '
PATH=$PATH

HOSTNAME=`/bin/hostname`

export USER LOGNAME PS1 PATH


(5)查看etc/inittab文件内容,把tty2::askfirst:-/bin/sh修改成console::askfirst:-/bin/sh
否则会无限报错:can't open /dev/tty2: No such file or directory

sudo gedit etc/inittab
#first:run the system script file
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::ctrlaltdel:-/sbin/reboot
#umount all filesystem
::shutdown:/bin/umount -a -r
#restart init process
::restart:/sbin/init

9、制作ext4格式的SD卡根文件系统分区

思路是:把SD卡挂载在宿主机Ubuntu系统中,用fdisk命令将SD卡进行分区,将该分区格式化为ext4格式。然后把~/rootfs/rootfs/目录中的所有文件复制到该分区里。那么该分区就可以作为内核启动时挂载的根文件系统。
具体步骤如下:

  • (1)SD卡分区:请参考
    SD卡烧写Linux kernel——SD卡分区,并烧写uboot,kernel,DTB及filesystem
  • (2)烧写u-boot:本质上用dd命令烧写。我是用Samsung提供的烧写脚本,自动烧写的。不会影响其他分区里的文件。原理请参考以下博文:
    Ubuntu18.04烧录U-Boot时格式化SD卡的文件类型(RAW 格式)
  • (3)复制根文件系统:思路是在/mnt目录下新建2个文件夹/mnt/sd1,然后挂载SD卡分区。先查看SD卡分区的设备名称,然后挂载,再复制,最后解挂。
    我用的是读卡器插入TF卡。对SD卡做了3个分区,用cat /proc/partitions,可以看到三个sdb,分别是/dev/sdb,/dev/sdb1,/dev/sdb2,/dev/sdb3。看官根据自己的实际情况,也可能是/dev/sdc、/dev/sdd等。
sudo mount -t /dev/sdb3 /mnt/sd1
sudo cp ~/rootfs/rootfs/* -a /mnt/sd1
ls /mnt/sd1
cd 
sudo umount /mnt/sd1

ls /mnt/sd1就可以看到根文件系统中的目录了。

三、启动exynos4412

把SD卡插入exyno4412开发板,设置启动引脚为SD卡启动。上电以后,就可以启动u-boot。通过cuteCom串口助手,输入u-boot命令,设置u-boot的环境变量,然后tftp下载Linux内核和设备树,bootm命令启动内核从SD卡挂载根文件系统。

setenv bootargs root=/dev/mmcblk0p3  rootfstype=ext4 init=/linuxrc console=ttySAC0,115200n8 earlyprintk
tftp 50000000 uImage;tftp 52000000 exynos4412-cbt4412.dtb;bootm 50000000 - 52000000

启动信息如下:

[11:04:26:842] CBT4412 #setenv bootargs root=/dev/mmcblk0p3  rootfstype=ext4 init=/linuxrc console=ttySAC0,115200n8 earlyprintk␍␊
[11:04:43:363] CBT4412 #bootm 50000000 - 52000000␍␊
[11:04:48:154] ## Booting kernel from Legacy Image at 50000000 ...␍␊
[11:04:48:162]    Image Name:   Linux-5.4.174␍␊
[11:04:48:162]    Image Type:   ARM Linux Kernel Image (uncompressed)␍␊
[11:04:48:170]    Data Size:    6783744 Bytes = 6.5 MiB␍␊
[11:04:48:186]    Load Address: 40008000␍␊
[11:04:48:186]    Entry Point:  40008000␍␊
[11:04:48:186]    Verifying Checksum ... OK␍␊
[11:04:49:032] ## Flattened Device Tree blob at 52000000␍␊
[11:04:49:048]    Booting using the fdt blob at 0x52000000␍␊
[11:04:49:048]    Loading Kernel Image␍␊
[11:04:49:215]    Loading Device Tree to 7ae83000, end 7ae91a72 ... OK␍␊
[11:04:49:254] ␍␊
[11:04:49:254] Starting kernel ...␍␊
[11:04:49:254] ␍␊
[11:04:49:667] [    0.000000][    T0] Booting Linux on physical CPU 0xa00␍␊
[11:04:49:671] [    0.000000][    T0] Linux version 5.4.174 (exynos4412@exynos4412) (gcc version 10.3.1 20210621 (GNU Toolchain for the A-profile Architecture 10.3-2021.07 (arm-10.29))) #1 SMP PREEMPT Sat Jan 29 08:02:13 CST 2022␍␊
[11:04:49:691] [    0.000000][    T0] CPU: ARMv7 Processor [413fc090] revision 0 (ARMv7), cr=10c5387d␍␊
[11:04:49:710] [    0.000000][    T0] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache␍␊
[11:04:49:710] [    0.000000][    T0] OF: fdt: Machine model: CBt4412 board based on Exynos4412␍␊
[11:04:49:723] [    0.000000][    T0] printk: bootconsole [earlycon0] enabled␍␊
[11:04:49:723] [    0.000000][    T0] Memory policy: Data cache writealloc␍␊
[11:04:49:723] [    0.000000][    T0] Reserved memory: created DMA memory pool at 0x7f800000, size 8 MiB␍␊
[11:04:49:737] [    0.000000][    T0] OF: reserved mem: initialized node region_mfc_right, compatible id shared-dma-pool␍␊
[11:04:49:745] [    0.000000][    T0] Reserved memory: created DMA memory pool at 0x7d400000, size 36 MiB␍␊
[11:04:49:758] [    0.000000][    T0] OF: reserved mem: initialized node region_mfc_left, compatible id shared-dma-pool␍␊
[11:04:49:768] [    0.000000][    T0] cma: Reserved 96 MiB at 0x74c00000␍␊
[11:04:49:768] [    0.000000][    T0] Samsung CPU ID: 0xe4412011␍␊
[11:04:49:792] [    0.000000][    T0] percpu: Embedded 20 pages/cpu s51048 r8192 d22680 u81920␍␊
[11:04:49:798] [    0.000000][    T0] Built 1 zonelists, mobility grouping on.  Total pages: 249344␍␊
[11:04:49:808] [    0.000000][    T0] Kernel command line: root=/dev/mmcblk0p3 rootfstype=ext4 init=/linuxrc console=ttySAC0,115200n8 earlyprintk␍␊
# 省略部分信息
[11:04:50:114] [    0.000000][    T0] random: get_random_bytes called from start_kernel+0x32c/0x4d8 with crng_init=0␍␊
[11:04:50:124] [    0.000000][    T0] Exynos4x12 clocks: sclk_apll = 1400000000, sclk_mpll = 800000000␍␊
[11:04:50:144] [    0.000000][    T0] ⇥	sclk_epll = 416000000, sclk_vpll = 440062500, arm_clk = 1400000000␍␊
# 省略
[11:04:52:010] [    1.826910][    T1] mmc_host mmc1: card is polling.␍␊
[11:04:52:010] [    1.843988][    T1] mmc_host mmc1: Bus speed (slot 0) = 50000000Hz (slot req 400000Hz, actual 396825HZ div = 63)␍␊
[11:04:52:024] [    1.866196][   T32] mmc0: new high speed SD card at address 0002␍␊
[11:04:52:034] [    1.875090][   T32] mmcblk0: mmc0:0002       968 MiB ␍␊
[11:04:52:047] [    1.882917][   T32]  mmcblk0: p1 p2 p3␍␊
# 省略
[11:04:52:358] [    2.200941][    T1] EXT4-fs (mmcblk0p3): mounted filesystem with ordered data mode. Opts: (null)␍␊
[11:04:52:367] [    2.201333][    T1] VFS: Mounted root (ext4 filesystem) readonly on device 179:3.␍␊
[11:04:52:376] [    2.203322][    T1] devtmpfs: mounted␍␊
[11:04:52:388] [    2.204978][    T1] Freeing unused kernel memory: 1024K␍␊
[11:04:52:397] [    2.239574][    T1] Run /linuxrc as init process␍␊
[11:04:52:610] ␍can't run '/etc/init.d/rcS': No such file or directory␍␊
[11:04:52:613] ␍␊
[11:04:52:613] Please press Enter to activate this console. 

从启动信息中我们可以看到SD卡的分区是mmcblk0p3。各位看官根据自己的实际情况修改bootargs参数中的root=/dev/mmcblk0p3

最后,记录一下制作ext2格式的ramdisk文件映像的方法。以下内容也可以不看。
(1)制作一个大小为8MB的映像文件,命令如下。

 sudo dd if=/dev/zero of=ramdisk bs=1k count=10240

此时可生成一个名为ramdisk的文件,文件大小为10MB。
(2)格式化文件成ext2文件格式,命令如下。

sudo mkfs ext2 -F ramdisk

(3)在mnt目录下创建initrd目录作为挂载点,并将ramdisk文件挂载上,命令如下。

sudo mkdir /mnt/initrd
sudo mount -t ext2 -o loop ramdisk /mnt/initrd

(4)将根文件系统里的内容全部复制到/mnt/initrd目录下,命令如下。

cp _install/* /mnt/initrd -a

(5)卸载挂载点initrd,命令如下。

sudo umount /mnt/initrd

(6)用管理员权限压缩ramdisk为ramdisk.gz,命令如下。

sudo su -
sudo gzip --best -c ramdisk > ramdisk.gz

(7)格式化为Uboot识别的格式,命令如下。

sudo mkimage -n "ramdisk" -A arm -O linux -T ramdisk -C gzip -a 0x41000000 -e 0x41000000 -d ramdisk.gz ramdisk.img

此时生成了一个名为ramdisk.img的文件,这个文件就是根文件系统映像文件。

Image Name:   ramdisk
Created:      Wed Jan 26 09:49:41 2022
Image Type:   ARM Linux RAMDisk Image (gzip compressed)
Data Size:    3047940 Bytes = 2976.50 KiB = 2.91 MiB
Load Address: 41000000
Entry Point:  41000000

如果提示错误:mkimage: Can't open ramdisk.: No such file or directory,那么先安装u-boot-tools。

sudo apt-get install u-boot-tools

  1. 根文件系统的两种格式 ↩︎

  2. 嵌入式 Linux根文件系统移植(二)——根文件系统简介 ↩︎

上一篇:SQL Server 查询数据库中所有的表名及行数


下一篇:微博feed系统的推(push)模式和拉(pull)模式和时间分区拉模式架构探讨