【教程】迅为iTOP-4412精英版exynos4412开发板搭建原生Linux最小系统(五)

[上一篇]

编译能在板子上运行的GCC编译器和GDB调试器
在板子上运行Java
使用ntp软件包通过网络更新系统时间
安装Bash shell

见GCC编译器专题文章:【教程】制作能在ARM板上直接运行的gcc本地编译器_ZLK1214的专栏-CSDN博客编译好的程序的下载链接:百度网盘 请输入提取码(提取码:ocmm)概述通常情况下,我们是在电脑里面开一个Linux虚拟机, 在虚拟机里面用交叉编译工具链编译好可执行文件后,将可执行文件拷贝到板子里面运行。也就是说,我们是用电脑上运行的gcc编译ARM板子上运行的程序,即交叉编译。对于用automake制作的软件包来说,有时候交叉编译会非常麻烦。Linux系统下绝大部分软件包都是automake制作的。交叉编译小型的软件,如make、file、mlocate这种还比较简单。但是如果是apache、https://blog.csdn.net/ZLK1214/article/details/120262773#comments_18310715

RamDisk的使用

Linux支持一种叫做RamDisk的技术,这种技术能将内存的一部分区域映射为磁盘空间,挂载到文件系统上。访问这种内存文件系统,比访问SD卡和emmc Flash快得多,但其中的文件掉电后会丢失。

格式化RamDisk并挂载

Linux系统启动后,/dev目录中共有16个ramdisk设备文件:ram0~15。

[root@exynos4412 /]# ls /dev/ram*
/dev/ram0   /dev/ram11  /dev/ram14  /dev/ram3   /dev/ram6   /dev/ram9
/dev/ram1   /dev/ram12  /dev/ram15  /dev/ram4   /dev/ram7
/dev/ram10  /dev/ram13  /dev/ram2   /dev/ram5   /dev/ram8

这些ramdisk设备文件初始内容为全0,容量为8MB。我们可以用od命令查看设备文件的内容:

[root@exynos4412 /]# od -x /dev/ram0
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
40000000

中间的省略号省略了后面所有的0,最后的地址是040000000(八进制数)=8388608(十进制数),说明这个ramdisk的容量为8388608字节,也就是8MB。
要使用这个ramdisk,我们需要先格式化,然后再用mount命令挂载。
我们用mke2fs命令将ramdisk格式化成ext2文件系统:

[root@exynos4412 /]# mke2fs -vm0 /dev/ram0 8192
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
2048 inodes, 8192 blocks
0 blocks (0%) reserved for the super user
First data block=1
Maximum filesystem blocks=262144
1 block groups
8192 blocks per group, 8192 fragments per group
2048 inodes per group

其中,-v是显示详细信息(verbose)的意思,-m0表示保留0%的块(不保留任何块)给管理员。8192指定的是创建的文件系统的大小(块数),这个大小可以任意指定,只要小于或等于ramdisk的容量就行。
格式化完成后,我们就可以用mount命令挂载ramdisk到文件系统里面了:

[root@exynos4412 /]# cd /mnt
[root@exynos4412 mnt]# mkdir myram0
[root@exynos4412 mnt]# mount /dev/ram0 myram0 -t ext2

可以用不带参数的mount命令查看当前已挂载的文件系统:

[root@exynos4412 mnt]# mount
/dev/root on / type ext4 (rw,relatime,data=ordered)
devtmpfs on /dev type devtmpfs (rw,relatime,size=437836k,nr_inodes=109459,mode=755)
proc on /proc type proc (rw,relatime)
tmpfs on /tmp type tmpfs (rw,relatime)
sysfs on /sys type sysfs (rw,relatime)
/dev/mmcblk0p3 on /mnt/myfat type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro)
devpts on /dev/pts type devpts (rw,relatime,mode=600,ptmxmode=000)
/dev/ram0 on /mnt/myram0 type ext2 (rw,relatime,errors=continue)
[root@exynos4412 mnt]# ls -l myram0
total 12
drwxr-xr-x    2 root     root         12288 Sep 26 08:28 lost+found

可以看到,里面已经有/dev/ram0了,文件系统为ext2。
进入挂载的文件夹,可以看到里面有一个lost+found文件夹。
FTP访问ramdisk:ftp://192.168.1.9/mnt/myram0/
可复制文件进去,文件大小不能超过文件系统的大小(8MB)。

使用df命令可以看到各个磁盘的容量,已用空间和剩余空间:
(笔者换了一张256GB的内存卡,重新分了区,rootfs分区的大小为219.9GB,myfat分区的大小为7.1GB)

[root@exynos4412 myram0]# df -h
Filesystem                Size      Used Available Use% Mounted on
/dev/root               219.9G     27.9G    180.8G  13% /
devtmpfs                427.6M         0    427.6M   0% /dev
tmpfs                   476.1M         0    476.1M   0% /tmp
/dev/mmcblk0p3            7.1G     32.0K      7.1G   0% /mnt/myfat
/dev/ram0                 7.7M     13.0K      7.7M   0% /mnt/myram0

取消挂载ramdisk:umount myram0

制作成镜像文件

挂载ram0:mount /dev/ram0 /mnt/myram0
复制一些文件进去(比如cp -r ~/myhello /mnt/myram0)
取消挂载ram0:umount /mnt/myram0
制作成镜像文件:gzip -cv9 /dev/ram0 > /tmp/ram_image.gz
其中-c是不删除原始文件,-v是显示详细信息,-9是以最好的压缩方式压缩。
生成的镜像文件是一个gzip压缩文件,解包后的大小为8MB。

试着用做好的镜像文件挂载/dev/ram1:
gunzip -c /tmp/ram_image.gz > /dev/ram1
mkdir /mnt/myram1
mount /dev/ram1 /mnt/myram1 -t ext2
挂载完成后,可以看到myram1里面也有我们复制进去的文件,说明挂载成功了。

调整RamDisk的大小

默认情况下,RamDisk的数量是16个,每个的大小是8MB。有时候8MB是不够用的,想要改得更大的话,可以修改uboot中的内核启动参数。
目前,我们的uboot的bootargs环境变量的值为console=ttySAC2,115200n8 earlyprintk root=/dev/mmcblk0p2 rw rootfstype=ext4 init=/linuxrc。
我们可以在后面追加一个ramdisk_size=X参数,其中X是每个RamDisk的大小,单位为KB。
比如修改成console=ttySAC2,115200n8 earlyprintk root=/dev/ram0 rw rootfstype=ext4 init=/linuxrc ramdisk_size=16384,然后启动内核:
提示:如果发现uboot里面粘贴命令后,命令执行不成功,重新手动输入一下就行了,有可能是串口调试工具的bug。

U-Boot 2017.11 (Aug 28 2021 - 11:13:25 +0800) for itop-4412

CPU:   Exynos4412 @ 1 GHz
Model: itop-4412 based on Exynos4412
Board: itop-4412 based on Exynos4412
DRAM:  1 GiB
WARNING: Caches not enabled
MMC:   SAMSUNG SDHCI: 0
*** Warning - bad CRC, using default environment

Hit any key to stop autoboot:  0
u-boot # printenv bootargs
bootargs=console=ttySAC2,115200n8 earlyprintk root=/dev/mmcblk0p2 rw rootfstype=ext4 init=/linuxrc
u-boot # setenv bootargs console=ttySAC2,115200n8 earlyprintk root=/dev/mmcblk0p2 rw rootfstype=ext4 init=/linuxrc ramdisk_size=16384
u-boot # printenv bootargs
bootargs=console=ttySAC2,115200n8 earlyprintk root=/dev/mmcblk0p2 rw rootfstype=ext4 init=/linuxrc ramdisk_size=16384
u-boot # boot

MMC read: dev # 0, block # 2048, count 160 ... 160 blocks read: OK

MMC read: dev # 0, block # 4096, count 16384 ... 16384 blocks read: OK
## Booting kernel from Legacy Image at 40007000 ...
   Image Name:   Linux-4.14.2
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    5517512 Bytes = 5.3 MiB
   Load Address: 40007000
   Entry Point:  40007000
   Verifying Checksum ... OK
## Flattened Device Tree blob at 41000000
   Booting using the fdt blob at 0x41000000
   Loading Kernel Image ... OK
   Loading Device Tree to 4ffef000, end 4ffffa22 ... OK

Starting kernel ...

Uncompressing Linux... done, booting the kernel.
s3c24xx_serial_console_setup: co=c0d28850 (2), 115200n8

(中间内容省略)

Please press Enter to activate this console. udhcpc: sending discover


Processing /etc/profile...
Done

[root@exynos4412 /]#
[root@exynos4412 /]# od -x /dev/ram0
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
100000000
[root@exynos4412 /]# od -x /dev/ram13
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
100000000
[root@exynos4412 /]#

内核启动后,可以用od命令看到,所有的ram盘的容量都变成了16MB。八进制0100000000=十进制16777216=16x1024x1024,就是16MB。
另外bootargs还支持一个mem参数,不要和ramdisk_size混淆了。这个mem参数设定的是Linux内核使用的内存的大小。如果我们添加一个mem=256M的参数,将内存容量设为256MB:
console=ttySAC2,115200n8 earlyprintk root=/dev/mmcblk0p2 rw rootfstype=ext4 init=/linuxrc mem=256M
那么启动后cat /proc/meminfo,可以看到MemTotal减小了,减小到了195076 kB。

RamDisk用作根文件系统

网吧和学校机房里面的电脑都有这样的特点:
(1)重启电脑后,系统盘(C盘)就会恢复原状,里面新建的所有文件都会丢失
(2)桌面也属于C盘,所以重启后桌面上的文件也会丢失
(3)其他盘(比如E盘)不受影响,重启后文件还在
这是因为网吧、学校的电脑的C盘采取了特殊处理,C盘是完全在内存里面,掉电就会丢失。
我们家用的电脑就不会有这样的情况,重启后C盘的文件,桌面的文件都不会丢失。

利用RamDisk,我们也可以让板子像网吧和学校的电脑那样,根文件系统(rootfs)位于内存里面,一断电就丢失,一重启就复原。然后/mnt目录下挂载几个可以永久保存数据的U盘。
为了方便应用程序开发,我们可以放置一个启动脚本startup.sh到不丢失数据的U盘里面。根文件系统的开机脚本是/etc/init.d/rcS,每次开机后,在这个脚本里面挂载U盘,然后执行U盘里面的startup.sh脚本,做一些事情,比如复制正在调试的程序和库到根文件系统里面,然后运行程序。

建立根文件系统镜像

打开电脑上的Fedora 34虚拟机,找到我们之前的静态链接版本的busybox根文件系统的源文件夹:
cd /home/oct1158/Downloads/busybox-1.26.2
其中_install文件夹保存的就是我们做好的根文件系统的全部文件。
建立一个16MB的空白ext4镜像文件:
mkdir _image
cd _image
mke2fs -t ext4 -vm0 busybox.img 16384

迅为教程里面说的是打包文件系统要用make_ext4fs工具,这个工具是安卓里面的,无法通过dnf install安装。本教程不是讲安卓的教程,一切关于安卓的东西都要避开。
我们用mke2fs命令建立好busybox.img空白镜像文件后,用系统自带的mount命令就能挂载这个镜像文件,然后就可以往里面复制文件了。

(下面这几条命令必须加上sudo,保证根文件系统所有的文件都是root权限)
挂载镜像文件:
sudo mkdir disk
sudo mount busybox.img disk
将busybox的安装文件复制到镜像中:
sudo cp -r ../_install/* disk
取消挂载镜像文件:
sudo umount busybox.img
以后如果要修改busybox.img镜像里面的文件,那就mount重新挂载,修改完文件后,umount取消挂载,多简单啊!

现在根文件系统的镜像文件busybox.img已经做好了,大小刚好为16777216字节=16MB。
如果我们SD卡里面Linux内核镜像烧写的是zImage,那么我们就可以用bootz命令启动了:
bootz ${zImage的地址} ${busybox.img的地址}  ${myexynos4412-itop-elite.dtb的地址}
但是我们实际用的是bootm命令,Linux内核镜像选择的是uImage:
bootm ${uImage的地址} ${busybox.uimg的地址}  ${myexynos4412-itop-elite.dtb的地址}
换句话说,就是Linux内核镜像有uImage和zImage之分,BusyBox根文件系统镜像也有busybox.uimg和busybox.img之分。

要想将busybox.img转换成busybox.uimg,我们需要mkimage工具,这个工具可以通过sudo dnf install uboot-tools安装。
制作busybox.uimg:
mkimage busybox.uimg -d busybox.img -n "BusyBox-1.26.2" -A arm -O linux -T ramdisk -C none -a 0x48000000 -e 0x48000000
其中0x48000000是uboot启动内核时,busybox.uimg在内存中的位置,可以自定义。

[oct1158@fedora _image]$ mkimage busybox.uimg -d busybox.img -n "BusyBox-1.26.2" -A arm -O linux -T ramdisk -C none -a 0x48000000 -e 0x48000000
Image Name:   BusyBox-1.26.2
Created:      Mon Sep 27 17:58:35 2021
Image Type:   ARM Linux RAMDisk Image (uncompressed)
Data Size:    16777216 Bytes = 16384.00 KiB = 16.00 MiB
Load Address: 48000000
Entry Point:  48000000

好了,busybox.uimg也做好了。我们把它烧写到SD卡里面,扇区号我们选择20480(可以任选)。
sudo dd iflag=dsync oflag=dsync if=busybox.uimg of=/dev/sdb seek=20480
提示:由于文件比较大,烧写时间会比较长(2~3分钟),请耐心等待。
操作前最好先备份一下SD卡的分区表(0号扇区),以免忘记写seek参数导致SD卡的分区表被破坏。

[oct1158@fedora _image]$ sudo dd iflag=dsync oflag=dsync if=busybox.uimg of=/dev/sdb seek=20480
[sudo] password for oct1158: 
32768+1 records in
32768+1 records out
16777280 bytes (17 MB, 16 MiB) copied, 120.92 s, 139 kB/s

烧写完成后,安全弹出SD卡。
把SD卡插到板子上,现在我们要修改uboot环境变量中的启动参数。

setenv ramdiskaddr 0x48000000
setenv bootargs console=ttySAC2,115200n8 earlyprintk root=/dev/ram0 rw rootfstype=ext4 init=/linuxrc ramdisk_size=16384
setenv bootcmd "mmc read ${dtbaddr} 0x800 0xa0\;mmc read ${loadaddr} 0x1000 0x4000\;mmc read ${ramdiskaddr} 0x5000 0x8001\;bootm ${loadaddr} ${ramdiskaddr} ${dtbaddr}"
saveenv(永久保存环境变量到flash,这一步可选)
boot

bootargs里面,我们将root由/dev/mmcblk0p2修改成了/dev/ram0,新增了ramdisk_size选项指定ramdisk的容量。
bootcmd里面,新增了mmc read ${ramdiskaddr} 0x5000 0x8001这条命令,意思是从第0x5000(十进制20480)扇区开始读SD卡,读0x8001(十进制32769)个扇区,将读出的内容放到${ramdiskaddr}(0x48000000)内存处。另外还修改了bootm的第二个参数,本来是一个横杠,现在修改成了ramdisk在内存中的地址。
为什么是读32769个扇区,不是32768呢?这是因为busybox.uimg文件的大小为16777280字节,比16777216字节(16MB)还多64字节,需要32769个扇区才能装下。
设置完环境变量后,执行saveenv保存,然后执行boot命令启动内核。
启动时,可以看到多了一栏BusyBox-1.26.2:

U-Boot 2017.11 (Aug 28 2021 - 11:13:25 +0800) for itop-4412

CPU:   Exynos4412 @ 1 GHz
Model: itop-4412 based on Exynos4412
Board: itop-4412 based on Exynos4412
DRAM:  1 GiB
WARNING: Caches not enabled
MMC:   SAMSUNG SDHCI: 0
Hit any key to stop autoboot:  0
u-boot # setenv ramdiskaddr 0x48000000
u-boot # setenv bootargs console=ttySAC2,115200n8 earlyprintk root=/dev/ram0 rw rootfstype=ext4 init=/linuxrc ramdisk_size=16384
u-boot # setenv bootcmd "mmc read ${dtbaddr} 0x800 0xa0\;mmc read ${loadaddr} 0x1000 0x4000\;mmc read ${ramdiskaddr} 0x5000 0x8001\;bootm ${loadaddr} ${ramdiskaddr} ${dtbaddr}"
u-boot # printenv ramdiskaddr
ramdiskaddr=0x48000000
u-boot # printenv bootargs
bootargs=console=ttySAC2,115200n8 earlyprintk root=/dev/ram0 rw rootfstype=ext4 init=/linuxrc ramdisk_size=16384
u-boot # printenv bootcmd
bootcmd=mmc read 0x41000000 0x800 0xa0;mmc read 0x40007000 0x1000 0x4000;mmc read 0x48000000 0x5000 0x8001;bootm 0x40007000 0x48000000 0x41000000
u-boot # saveenv
Saving Environment to MMC...
Writing to MMC(0)... done
u-boot # boot

MMC read: dev # 0, block # 2048, count 160 ... 160 blocks read: OK

MMC read: dev # 0, block # 4096, count 16384 ... 16384 blocks read: OK

MMC read: dev # 0, block # 20480, count 32769 ... 32769 blocks read: OK
## Booting kernel from Legacy Image at 40007000 ...
   Image Name:   Linux-4.14.2
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    5517512 Bytes = 5.3 MiB
   Load Address: 40007000
   Entry Point:  40007000
   Verifying Checksum ... OK
## Loading init Ramdisk from Legacy Image at 48000000 ...
   Image Name:   BusyBox-1.26.2
   Image Type:   ARM Linux RAMDisk Image (uncompressed)
   Data Size:    16777216 Bytes = 16 MiB
   Load Address: 48000000
   Entry Point:  48000000
   Verifying Checksum ... OK
## Flattened Device Tree blob at 41000000
   Booting using the fdt blob at 0x41000000
   Loading Kernel Image ... OK
   Loading Ramdisk to 4f000000, end 50000000 ... OK
   Loading Device Tree to 4efef000, end 4efffa22 ... OK

Starting kernel ...

Uncompressing Linux... done, booting the kernel.
(中间内容省略)
Please press Enter to activate this console.

Processing /etc/profile... Done

/ #

启动ramdisk根文件系统成功。成功进入了linux shell命令行。

用df -h命令可以看到,根文件系统/dev/root已经是ramdisk了,容量为16MB。
在根文件系统里面创建的所有文件,关机后都会丢失。

使用压缩了的根文件系统镜像

我们做的镜像才16MB,烧写到SD卡就已经要花两三分钟了,要是我们能像之前ram盘那样用gzip压缩一下就好了。
我们回到Fedora 34虚拟机里面,用gzip命令压缩一下镜像文件:
gzip -kv9 busybox.img
这里-k也是不删除原始文件的意思。压缩后得到busybox.img.gz,大小只有945KB。

然后,我们再用mkimage命令将busybox.img.gz转换成busybox.uimg。因为转换的是压缩文件,所以-C后面填的不再是none,而是gzip:
mkimage busybox.uimg -d busybox.img.gz -n "BusyBox-1.26.2" -A arm -O linux -T ramdisk -C gzip -a 0x48000000 -e 0x48000000

[oct1158@fedora _image]$ mkimage busybox.uimg -d busybox.img.gz -n "BusyBox-1.26.2" -A arm -O linux -T ramdisk -C gzip -a 0x48000000 -e 0x48000000
Image Name:   BusyBox-1.26.2
Created:      Mon Sep 27 19:03:09 2021
Image Type:   ARM Linux RAMDisk Image (gzip compressed)
Data Size:    944967 Bytes = 922.82 KiB = 0.90 MiB
Load Address: 48000000
Entry Point:  48000000

生成busybox.uimg后,烧写到SD卡里面(注意千万不要漏掉seek参数)
sudo dd iflag=dsync oflag=dsync if=busybox.uimg of=/dev/sdb seek=20480

[oct1158@fedora _image]$ sudo dd iflag=dsync oflag=dsync if=busybox.uimg of=/dev/sdb seek=20480
[sudo] password for oct1158: 
1845+1 records in
1845+1 records out
945053 bytes (945 kB, 923 KiB) copied, 7.26208 s, 130 kB/s

这下烧写快很多了,7秒就完成了。
烧写完后安全弹出SD卡,将SD卡插入到板子上,不改变uboot启动参数,也能启动成功。uboot能自动识别gzip压缩文件。

U-Boot 2017.11 (Aug 28 2021 - 11:13:25 +0800) for itop-4412

CPU:   Exynos4412 @ 1 GHz
Model: itop-4412 based on Exynos4412
Board: itop-4412 based on Exynos4412
DRAM:  1 GiB
WARNING: Caches not enabled
MMC:   SAMSUNG SDHCI: 0
Hit any key to stop autoboot:  0

MMC read: dev # 0, block # 2048, count 160 ... 160 blocks read: OK

MMC read: dev # 0, block # 4096, count 16384 ... 16384 blocks read: OK

MMC read: dev # 0, block # 20480, count 32769 ... 32769 blocks read: OK
## Booting kernel from Legacy Image at 40007000 ...
   Image Name:   Linux-4.14.2
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    5517512 Bytes = 5.3 MiB
   Load Address: 40007000
   Entry Point:  40007000
   Verifying Checksum ... OK
## Loading init Ramdisk from Legacy Image at 48000000 ...
   Image Name:   BusyBox-1.26.2
   Image Type:   ARM Linux RAMDisk Image (gzip compressed)
   Data Size:    944967 Bytes = 922.8 KiB
   Load Address: 48000000
   Entry Point:  48000000
   Verifying Checksum ... OK
## Flattened Device Tree blob at 41000000
   Booting using the fdt blob at 0x41000000
   Loading Kernel Image ... OK
   Loading Ramdisk to 4ff19000, end 4ffffb47 ... OK
   Loading Device Tree to 4ff08000, end 4ff18a22 ... OK

Starting kernel ...

Uncompressing Linux... done, booting the kernel.

现在BusyBox那一栏变成了(gzip compressed)。

运行C和C++程序

16MB的ramdisk太小,我们先重新做下镜像,容量改成128MB:
mke2fs -t ext4 -vm0 busybox.img 131072
sudo mount busybox.img disk
sudo cp -r ../_install/* disk
sudo umount busybox.img
gzip -kv9 busybox.img
mkimage busybox.uimg -d busybox.img.gz -n "BusyBox-1.26.2" -A arm -O linux -T ramdisk -C gzip -a 0x48000000 -e 0x48000000
sudo dd iflag=dsync oflag=dsync if=busybox.uimg of=/dev/sdb seek=20480
修改uboot的bootargs参数:

u-boot # setenv bootargs console=ttySAC2,115200n8 earlyprintk root=/dev/ram0 rw rootfstype=ext4 init=/linuxrc ramdisk_size=131072
u-boot # saveenv
Saving Environment to MMC...
Writing to MMC(0)... done
u-boot # boot

ramdisk的容量比较小,交叉编译器libc里面所有的库加起来又太大,很占空间。我们必须学会裁剪lib库,只把需要的库放到根文件系统的镜像里面。
我们把以前SD卡上的Linux根文件系统分区挂载到/mnt/sdlinux下,把里面的程序拷出来运行:

/ # mkdir /mnt/sdlinux
/ # mount /dev/mmcblk0p2 /mnt/sdlinux -t ext4
[  100.383992] EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. Opts: (null)
/ # cp -r /mnt/sdlinux/root/libtest /root
/ # cp /mnt/sdlinux/root/hello_dynamic/hello /root/libtest
/ # cd /root/libtest
/root/libtest # ls
Makefile      cpptest.cpp   mathtest      threadtest
cpptest       hello         mathtest.c    threadtest.c

里面有四个程序:hello、cpptest、mathtest和threadtest。
我们先运行一下hello:
/root/libtest # ./hello
-/bin/sh: ./hello: not found
提示not found,这是因为根文件系统里面缺少库文件。缺少什么库文件呢?我们在Fedora 34虚拟机里面用file命令看看呢:
[oct1158@fedora hello_dynamic]$ file hello
hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, with debug_info, not stripped
因此缺少的库文件是/lib/ld-linux-armhf.so.3,于是从sdlinux里面复制过来。
提示:复制前,最好先在虚拟机里面看一下,要复制的到底是软链接,还是真实的文件。复制时最好带上-r参数,以免软链接变成真实的文件。

/root/libtest # cp -r /mnt/sdlinux/lib/ld-linux-armhf.so.3 /lib
/root/libtest # ls -l /lib
total 0
lrwxrwxrwx    1 0        0               10 Jan  1 00:17 ld-linux-armhf.so.3 -> ld-2.33.so

复制完发现只是一个软链接,链接的对象ld-2.33.so是红字,说明是死链。还需要把链接对象复制过来:

/root/libtest # cp -r /mnt/sdlinux/lib/ld-2.33.so /lib
/root/libtest # ls -l /lib
total 1072
-rwxr-xr-x    1 0        0          1097308 Jan  1 00:18 ld-2.33.so
lrwxrwxrwx    1 0        0               10 Jan  1 00:17 ld-linux-armhf.so.3 -> ld-2.33.so

再次运行程序:

/root/libtest # ./hello
./hello: error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory

还缺少libc.so.6库,继续复制:
(这一个库就10MB!)

/root/libtest # cp -r /mnt/sdlinux/lib/libc.so.6 /lib
/root/libtest # cp -r /mnt/sdlinux/lib/libc-2.33.so /lib
/root/libtest # df -h
Filesystem                Size      Used Available Use% Mounted on
/dev/root               120.0M     14.1M    103.3M  12% /
devtmpfs                427.1M         0    427.1M   0% /dev
tmpfs                   476.1M         0    476.1M   0% /tmp
/dev/mmcblk0p2          219.9G     27.9G    180.8G  13% /mnt/sdlinux

现在终于能运行成功了:

/root/libtest # ./hello
Hello World!
2*sin(1)*cos(1)=0.909297
sin(2)=0.909297
EFCDAB89
t=946686107
Time: 2000-01-01 00:21:47

这说明,hello程序的依赖项是/lib下的ld-2.33.so、libc-2.33.so、ld-linux-armhf.so.3和libc.so.6。
现在,运行其他三个程序,还在报错:

/root/libtest # ./mathtest
./mathtest: error while loading shared libraries: libm.so.6: cannot open shared object file: No such file or directory
/root/libtest # ./threadtest
./threadtest: error while loading shared libraries: libpthread.so.0: cannot open shared object file: No such file or directory
/root/libtest # ./cpptest
./cpptest: error while loading shared libraries: libstdc++.so.6: cannot open shared object file: No such file or directory

分别把这些库复制出来:

/root/libtest # cp -r /mnt/sdlinux/lib/libm.so.6 /lib
/root/libtest # cp -r /mnt/sdlinux/lib/libm-2.33.so /lib
/root/libtest # cp -r /mnt/sdlinux/lib/libpthread.so.0 /lib
/root/libtest # cp -r /mnt/sdlinux/lib/libpthread-2.33.so /lib
/root/libtest # cp -r /mnt/sdlinux/usr/lib/libstdc++.so.6 /usr/lib
/root/libtest # cp -r /mnt/sdlinux/usr/lib/libstdc++.so.6.0.28 /usr/lib

现在除了cpptest以外,其他三个程序都能运行了:

/root/libtest # ./mathtest
z=2+11i
z=2+1i
/root/libtest # ./threadtest
Hello World!
Hello Thread! arg=busybox
ret=end
/root/libtest # ./cpptest
./cpptest: error while loading shared libraries: libgcc_s.so.1: cannot open shared object file: No such file or directory

再继续复制libgcc_s.so.1:

/root/libtest # cp -r /mnt/sdlinux/usr/lib/libgcc_s.so.1 /usr/lib
/root/libtest # ./cpptest
hello world
len=11
/root/libtest # df -h
Filesystem                Size      Used Available Use% Mounted on
/dev/root               120.0M     35.7M     81.7M  30% /
devtmpfs                427.1M         0    427.1M   0% /dev
tmpfs                   476.1M         0    476.1M   0% /tmp
/dev/mmcblk0p2          219.9G     27.9G    180.8G  13% /mnt/sdlinux

好了,四个程序都能运行了。现在ramdisk已经用了35.7MB了。
/mnt/sdlinux/bin/busybox是动态链接版本的busybox程序,现在也能运行了。这说明,我们已经把基础库复制完了。

完善根文件系统镜像

现在我们根据上一讲的内容,将用户账户和网络功能添加到ramdisk根文件系统中。
回到Fedora 34虚拟机里面,挂载busybox.img镜像,准备修改里面的文件:
cd ~/Downloads/busybox-1.26.2/_image
sudo mount busybox.img disk
添加root用户和组,root密码为hickwall512:
sudo bash -c "echo 'root:x:0:root' > disk/etc/group"
sudo bash -c "echo 'root:SwDpipZI1TzEo:0:0:Linux User,,,:/root:/bin/sh' > disk/etc/passwd"
设置设备名:
sudo bash -c "echo exynos4412 > disk/etc/hostname"

修改启动脚本:
sudo vim disk/etc/init.d/rcS

#! /bin/sh

/bin/mount -a
/bin/hostname -F /etc/hostname

# 热插拔自动检测
mkdir /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

# 若插入了U盘, 则执行U盘里面的开机脚本
# 请注意脚本里面的换行符必须是\n, 不能是\r\n, 否则执行脚本会提示ash: xxx.sh: not found
if [ -x /mnt/sda1/autorun.sh ]
then
	/mnt/sda1/autorun.sh
fi

# DHCP获取IP地址
ifconfig lo up
udhcpc -b -x hostname:$(hostname) &

# 启动ftp和telnet服务器
tcpsvd 0 21 ftpd -w / &
telnetd

修改用户环境配置:
sudo vim disk/etc/profile

# /etc/profile: system-wide .profile file for the Bourne shells
 
echo
echo -n "Processing /etc/profile... "
# no-op
 
awktext="\$1==\"$USER\"{print \$6}"
export HOME=`awk -F: "$awktext" /etc/passwd`
export PS1="[\\u@\\h \\w]\\$ "

if [ -x /mnt/sda1/profile.sh ]
then
	source /mnt/sda1/profile.sh
fi

echo "Done"
echo

复制udhcpc脚本:
sudo mkdir -p disk/usr/share/udhcpc
sudo cp ~/Downloads/busybox-1.26.2/examples/udhcp/simple.script disk/usr/share/udhcpc/default.script

配置U盘热插拔检测:
sudo bash -c "echo 'sd[a-z][0-9]+   0:0 660 */etc/hotplug.sh' > disk/etc/mdev.conf"

sudo vim disk/etc/hotplug.sh
sudo chmod +x disk/etc/hotplug.sh

#!/bin/sh
if [ "$ACTION" = "add" ]
then
	mkdir -p /mnt/$MDEV
	mount /dev/$MDEV /mnt/$MDEV 2>/dev/null
else
	umount /mnt/$MDEV 2>/dev/null
	rmdir /mnt/$MDEV 2>/dev/null
fi

复制交叉编译器的libc库文件:
TOOLCHAIN=/home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/arm-none-linux-gnueabihf/libc
sudo cp -r $TOOLCHAIN/lib/ld-linux-armhf.so.3 disk/lib
sudo cp -r $TOOLCHAIN/lib/ld-2.33.so disk/lib
sudo cp -r $TOOLCHAIN/lib/libc.so.6 disk/lib
sudo cp -r $TOOLCHAIN/lib/libc-2.33.so disk/lib
sudo cp -r $TOOLCHAIN/lib/libm.so.6 disk/lib
sudo cp -r $TOOLCHAIN/lib/libm-2.33.so disk/lib
sudo cp -r $TOOLCHAIN/lib/libpthread.so.0 disk/lib
sudo cp -r $TOOLCHAIN/lib/libpthread-2.33.so disk/lib
sudo cp -r $TOOLCHAIN/usr/lib/libstdc++.so.6 disk/usr/lib
sudo cp -r $TOOLCHAIN/usr/lib/libstdc++.so.6.0.28 disk/usr/lib
sudo cp -r $TOOLCHAIN/usr/lib/libgcc_s.so.1 disk/usr/lib

复制支持dns域名解析所需要的库文件:
sudo cp -r $TOOLCHAIN/lib/libnss_dns-2.33.so disk/lib
sudo cp -r $TOOLCHAIN/lib/libnss_dns.so.2 disk/lib
sudo cp -r $TOOLCHAIN/lib/libresolv-2.33.so disk/lib
sudo cp -r $TOOLCHAIN/lib/libresolv.so.2 disk/lib

最后生成镜像文件:
sudo umount busybox.img
gzip -kv9 busybox.img
mkimage busybox.uimg -d busybox.img.gz -n "BusyBox-1.26.2" -A arm -O linux -T ramdisk -C gzip -a 0x48000000 -e 0x48000000

烧写到SD卡:
sudo dd iflag=dsync oflag=dsync if=busybox.uimg of=/dev/sdb seek=20480

提示:
(1)无论是静态链接版本还是动态链接版本的busybox,只要/lib中有以下几个库,dns域名解析就能正常工作,就能ping通域名
     ld-2.33.so           libc.so.6            libresolv-2.33.so
     ld-linux-armhf.so.3  libnss_dns-2.33.so   libresolv.so.2
     libc-2.33.so         libnss_dns.so.2
     静态链接版本的busybox,必须要有上述动态库,才能使用dns功能
     不过需要的库没有动态链接版本的busybox那么多,没有libm库,动态链接版本的busybox是无法运行的
(2)重新以动态链接方式编译busybox后只需替换/bin/busybox程序,因为其他的文件全部都是软链接
     cp ../_install/bin/busybox disk/bin/busybox

启动内核:

DNS功能正常,telnet和ftp服务器也能正常访问。

在U盘中放置开机脚本和环境变量配置脚本

根文件系统采用ramdisk后,里面的文件会在板子断电后全部丢失。要想修改一个文件,永久生效的话,就得重新制作镜像文件,然后烧写。
为了方便开发,我们可以在板子的USB口上插一个U盘,里面放一个开机脚本autorun.sh和环境变量配置脚本profile.sh。然后在镜像里面真正的开机脚本中/etc/init.d/rcS执行autorun.sh这个脚本,环境变量配置脚本/etc/profile里面source一下profile.sh。U盘里面的文件在板子断电后并不会丢失,板子再次通电后,执行开机脚本,把需要调试的程序从U盘拷回根文件系统,并执行环境变量配置脚本,配置好环境变量。
刚才完善根文件系统镜像的时候已经把这些代码加进去了,我们只需要在U盘上创建好autorun.sh和profile.sh就行了。
之所以要把环境变量单独放在profile.sh里面,是因为autorun.sh仅在开机时执行一次,而profile.sh不仅开机要执行,telnet客户端连接到板子后也要执行。

autorun.sh:

#!/bin/sh
echo "[AUTORUN] Hello World!"
/bin/busybox > /root/hello.txt

profile.sh:

#!/bin/sh
export TESTVAR="hello world"

U盘用的文件系统是FAT32,因此所有的脚本都有执行权限。
请注意这两个脚本的换行格式必须是Unix(\n)格式的,不能是Windows(\r\n)格式的,否则脚本无法执行成功,会提示ash: xxx.sh: not found。

好了,插上U盘,开机启动,就可以看到开机时输出了“[AUTORUN] Hello World!”一行字,/root下生成了hello.txt文件,echo $TESTVAR会输出hello world。如果不插U盘开机的话,就没有这些现象发生,也不会报错。

认识emmc存储器

迅为itop4412精英版上搭载了一颗容量为8GB的KLM8G1GETF emmc存储器。emmc存储器是一种NAND Flash,只不过他的通信接口采用的是SDIO,用法基本和SD内存卡一模一样,块大小也是512字节。
我们可以考虑将uboot、linux内核和设备树、busybox根文件系统放到板载的emmc存储器上,调整红色拨码开关,让板子开机时从emmc启动,这样板子就可以脱离SD内存卡运行了。

我们知道,从SD内存卡启动时,exynos4412首先是从1号扇区去加载uboot的。那如果是从emmc启动呢,是不是也还是从1号扇区去读呢?
让我们来看看下面这两张图。

从图上可以看出,板子从emmc启动时,读取的不是用户数据区的1号扇区,而是由ExtCSD寄存器的第179字节的第[5:3]位(BOOT_PARTITION_ENABLE)所决定的数据区域的0号扇区。PARTITION_CONFIG的默认值是0x48,也就是BOOT_PARTITION_ENABLE=0x01,对应的数据区域是boot0区(也叫boot partition 1)。

SD内存卡只有用户数据区(/dev/mmcblk0),但emmc不同。emmc除了用户数据区(/dev/mmcblk1)外,还有三个区:
boot0区(/dev/mmcblk1boot0)、boot1区(/dev/mmcblk1boot1)和rpmb区(/dev/mmcblk1rpmb)。

[root@exynos4412 /]# ls -l /dev/mmc*
brw-rw----    1 root     root      179,  64 Jan  1  1970 /dev/mmcblk0
brw-rw----    1 root     root      179,  65 Jan  1  1970 /dev/mmcblk0p1
brw-rw----    1 root     root      179,  66 Jan  1  1970 /dev/mmcblk0p2
brw-rw----    1 root     root      179,  67 Jan  1  1970 /dev/mmcblk0p3
brw-rw----    1 root     root      179,   0 Jan  1  1970 /dev/mmcblk1
brw-rw----    1 root     root      179,  16 Jan  1  1970 /dev/mmcblk1boot0
brw-rw----    1 root     root      179,  32 Jan  1  1970 /dev/mmcblk1boot1
brw-rw----    1 root     root      179,  48 Jan  1  1970 /dev/mmcblk1rpmb

emmc的用户数据区(/dev/mmcblk1)是用来存放用户数据的,用户可对这个区域再进行分区,形成/dev/mmcblk1p1、/dev/mmcblk1p2、/dev/mmcblk1p3等等子分区,分区表位于用户数据区的0号扇区。
boot0和boot1这两个区是用来存放启动代码的,rpmb区是用于存放加密数据的分区。
我们先来看看这些分区的大小。

用户空间:15269888扇区×512字节=7818182656字节=7456MB
[root@exynos4412 /]# cat /sys/block/mmcblk1/size
15269888
这和芯片datasheet上说的一样,确实是7818182656字节。

BOOT0、BOOT1空间:8192扇区×512字节=4194304字节=4MB
[root@exynos4412 /]# cat /sys/block/mmcblk1boot0/size
8192
[root@exynos4412 /]# cat /sys/block/mmcblk1boot1/size
8192

RPMB空间:1024扇区×512字节=524288字节=512KB
[root@exynos4412 /]# cat /sys/block/mmcblk1rpmb/size
1024

CID(Card IDentification)寄存器的值(128位):
[root@exynos4412 /]# cat /sys/block/mmcblk1/device/name
8GTF4R

OCR(Operation Condition Register)寄存器的值(32位):
[root@exynos4412 /]# cat /sys/block/mmcblk1/device/ocr
00040000

CSD(Card Specific Data)寄存器的值(128位):
[root@exynos4412 /]# cat /sys/block/mmcblk1/device/csd
d02701320f5903fff6dbffef8e40400d

要想读ExtCSD寄存器(512字节),需要编译安装mmc程序。
源码包下载地址:https://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc-utils.git/
这个网址有时候能下载,有时候不能下载。。

C:\Users\Octopus\Desktop\test>git clone https://git.kernel.org/pub/scm/linux/ker
nel/git/cjb/mmc-utils.git/
Cloning into 'mmc-utils'...
remote: Enumerating objects: 348, done.
remote: Total 348 (delta 0), reused 0 (delta 0), pack-reused 348Receiving object
Receiving objects:  62% (216/348)
Receiving objects: 100% (348/348), 114.03 KiB | 234.00 KiB/s, done.
Resolving deltas: 100% (219/219), done.

尝试了十几次,只有两次成功。
还好下载下来了,打包复制进linux虚拟机,准备交叉编译。

修改Makefile文件:
CC ?= gcc改成CC = /home/oct1158/Downloads/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc
CHECKFLAGS去掉-Werror
然后编译:make
生成安装文件:make install DESTDIR=$(pwd)/_install
生成的安装文件只有_install/usr/local/bin/mmc这一个文件,就懒得打包了。
直接把mmc上传到板子上,随便找一个地方(比如/root)运行。
编译好的程序的下载链接:百度网盘 请输入提取码(提取码:i2hk)

读一下extcsd寄存器,将boot有关的信息筛选出来:

[root@exynos4412 ~]# ./mmc extcsd read /dev/mmcblk1 | grep -i boot
Boot Information [BOOT_INFO: 0x07]
 Device supports alternative boot method
 Device supports dual data rate during boot
 Device supports high speed timing during boot
Boot partition size [BOOT_SIZE_MULTI: 0x20]
Boot configuration bytes [PARTITION_CONFIG: 0x48]
 Boot Partition 1 enabled
 No access to boot partition
Boot config protection [BOOT_CONFIG_PROT: 0x00]
Boot bus Conditions [BOOT_BUS_CONDITIONS: 0x01]
Boot write protection status registers [BOOT_WP_STATUS]: 0x00
Boot Area Write protection [BOOT_WP]: 0x00

其中“Boot Partition 1 enabled”这句话很关键,意思是说,启动区选择的是boot0。
我们可以选择其他的启动区,命令格式是mmc bootpart enable <boot_partition> <send_ack> <device>。
其中参数send_ack必须为1,不能为0,否则进不了uboot。
选择boot0空间:./mmc bootpart enable 1 1 /dev/mmcblk1
选择boot1空间:./mmc bootpart enable 2 1 /dev/mmcblk1
选择用户空间:./mmc bootpart enable 7 1 /dev/mmcblk1
或者禁止从emmc启动:./mmc bootpart enable 0 0 /dev/mmcblk1

但是笔者发现一个现象,在迅为itop4412板子上,配置完成后不能去读emmc的boot0或boot1空间,例如:
od -x /dev/mmcblk1boot0 -N512
od -x /dev/mmcblk1boot1 -N512
只要一读,马上PARTITION_CONFIG就会自动恢复成0x48(Boot Partition 1 enabled)。
所以,如果把boot0内容清零,boot1烧写成迅为光盘里的“04_镜像_QT文件系统/uboot/scp/1G DDR/u-boot-iTOP-4412.bin”,然后启动区修改成boot1,板子关机。接着把红色拨码开关拨到emmc(1-OFF,2-ON),板子开机,确实能进入U-Boot 2010.03 (Mar 20 2018 - 00:02:39) for iTOP-4412 Android。
但是关机,再开机,就进不了uboot了,因为读了emmc,马上又恢复成Boot Partition 1 enabled了,他跑去boot0区读uboot了,而boot0区是被清零了的,根本读不到,串口也就没有任何输出。
笔者推测这很可能是emmc驱动程序的问题,只要一去读,启动项马上被改成boot0。

清空emmc boot0区:
echo 0 > /sys/block/mmcblk1boot0/force_ro
dd if=/dev/zero of=/dev/mmcblk1boot0 count=8192

清空emmc boot1区:
echo 0 > /sys/block/mmcblk1boot1/force_ro
dd if=/dev/zero of=/dev/mmcblk1boot1 count=8192

将uboot烧写到emmc boot0区:
echo 0 > /sys/block/mmcblk1boot0/force_ro
dd if=u-boot-iTOP-4412.bin of=/dev/mmcblk1boot0

将uboot烧写到emmc boot1区:
echo 0 > /sys/block/mmcblk1boot1/force_ro
dd if=u-boot-iTOP-4412.bin of=/dev/mmcblk1boot1

boot0和boot1区有写保护,需要先将force_ro设为0,才能改写数据,否则dd命令会执行失败。

给emmc用户空间分区

Busybox自带了/sbin/fdisk分区工具,以及/sbin/mke2fs、/sbin/mkfs.vfat格式化磁盘的工具。下面来讲解这三个工具的用法。
查看emmc分区表:

[root@exynos4412 /]# fdisk -l /dev/mmcblk1
Disk /dev/mmcblk1: 7456 MB, 7818182656 bytes, 15269888 sectors
238592 cylinders, 4 heads, 16 sectors/track
Units: cylinders of 64 * 512 = 32768 bytes

Disk /dev/mmcblk1 doesn't contain a valid partition table

现在emmc完全为空,不含任何分区。我们启动/sbin/fdisk,给emmc分区:

[root@exynos4412 /]# fdisk /dev/mmcblk1
Device contains neither a valid DOS partition table, nor Sun, SGI, OSF or GPT disklabel
Building a new DOS disklabel. Changes will remain in memory only,
until you decide to write them. After that the previous content
won't be recoverable.


The number of cylinders for this disk is set to 238592.
There is nothing wrong with that, but this is larger than 1024,
and could in certain setups cause problems with:
1) software that runs at boot time (e.g., old versions of LILO)
2) booting and partitioning software from other OSs
   (e.g., DOS FDISK, OS/2 FDISK)

Command (m for help):

启动fdisk后,会创建一张空白的分区表。现在,我们先创建一个256MB的分区:

Command (m for help): n
Command action
   e   extended
   p   primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-238592, default 1): Using default value 1
Last cylinder or +size or +sizeM or +sizeK (1-238592, default 238592): +256M

再创建一个6500MB的分区:

Command (m for help): n
Command action
   e   extended
   p   primary partition (1-4)
p
Partition number (1-4): 2
First cylinder (7815-238592, default 7815): Using default value 7815
Last cylinder or +size or +sizeM or +sizeK (7815-238592, default 238592): +6500M

再创建最后一个分区,用完剩余的所有空间:

Command (m for help): n
Command action
   e   extended
   p   primary partition (1-4)
p
Partition number (1-4): 3
First cylinder (206180-238592, default 206180): Using default value 206180
Last cylinder or +size or +sizeM or +sizeK (206180-238592, default 238592): Using default value 238592

查看待写入的分区表:

Command (m for help): p
Disk /dev/mmcblk1: 7456 MB, 7818182656 bytes, 15269888 sectors
238592 cylinders, 4 heads, 16 sectors/track
Units: cylinders of 64 * 512 = 32768 bytes

Device       Boot StartCHS    EndCHS        StartLBA     EndLBA    Sectors  Size Id Type
/dev/mmcblk1p1    0,1,1       1023,3,16           16     500095     500080  244M 83 Linux
/dev/mmcblk1p2    1023,3,16   1023,3,16       500096   13195455   12695360 6198M 83 Linux
/dev/mmcblk1p3    1023,3,16   1023,3,16     13195456   15269887    2074432 1012M 83 Linux

写入分区表,完成分区:

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table
[ 8900.739772]  mmcblk1: p1 p2 p3

fdisk命令自动退出。再次查看分区表:

[root@exynos4412 /]# fdisk -l /dev/mmcblk1
Disk /dev/mmcblk1: 7456 MB, 7818182656 bytes, 15269888 sectors
238592 cylinders, 4 heads, 16 sectors/track
Units: cylinders of 64 * 512 = 32768 bytes

Device       Boot StartCHS    EndCHS        StartLBA     EndLBA    Sectors  Size Id Type
/dev/mmcblk1p1    0,1,1       1023,3,16           16     500095     500080  244M 83 Linux
/dev/mmcblk1p2    1023,3,16   1023,3,16       500096   13195455   12695360 6198M 83 Linux
/dev/mmcblk1p3    1023,3,16   1023,3,16     13195456   15269887    2074432 1012M 83 Linux

将emmc第二分区格式化为ext4文件系统:

[root@exynos4412 /]# mke2fs -vm0 /dev/mmcblk1p2 -T ext4
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
397488 inodes, 1586920 blocks
0 blocks (0%) reserved for the super user
First data block=0
Maximum filesystem blocks=4194304
49 block groups
32768 blocks per group, 32768 fragments per group
8112 inodes per group
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736

挂载emmc第二分区:

[root@exynos4412 /]# mkdir /mnt/mypart2
[root@exynos4412 /]# mount /dev/mmcblk1p2 /mnt/mypart2 -t ext4
[ 1597.246311] EXT4-fs (mmcblk1p2): mounted filesystem without journal. Opts: (null)
[root@exynos4412 /]# ls /mnt/mypart2
lost+found

将emmc第三分区格式化为FAT32文件系统:

[root@exynos4412 /]# mkfs.vfat -v /dev/mmcblk1p3
Device '/dev/mmcblk1p3':
heads:4, sectors/track:16, bytes/sector:512
media descriptor:f8
total sectors:2074432, clusters:258797, sectors/cluster:8
FATs:2, sectors/FAT:2022
volumeID:61692a28, label:''

挂载emmc第三分区:

[root@exynos4412 /]# mkdir /mnt/mypart3
[root@exynos4412 /]# mount /dev/mmcblk1p3 /mnt/mypart3
[root@exynos4412 /]# ls /mnt/mypart3

查看挂载了的所有磁盘的容量和剩余空间:

[root@exynos4412 /]# df -h
Filesystem                Size      Used Available Use% Mounted on
/dev/root               219.9G     28.9G    179.8G  14% /
devtmpfs                427.6M         0    427.6M   0% /dev
tmpfs                   476.1M         0    476.1M   0% /tmp
/dev/mmcblk0p3            7.1G     40.0K      7.1G   0% /mnt/myfat
/dev/mmcblk1p2            6.0G     20.0K      6.0G   0% /mnt/mypart2
/dev/mmcblk1p3         1010.9M      4.0K   1010.9M   0% /mnt/mypart3

从emmc启动uboot和linux内核

探究hyyoxhk移植的uboot-2017.11无法从eMMC启动的原因_ZLK1214的专栏-CSDN博客https://blog.csdn.net/ZLK1214/article/details/120755370

正点原子阿尔法I.MX6ULL开发板的做法

正点原子出厂默认系统的uboot环境变量内容非常复杂,这里详细讲解一下。

=> printenv
baudrate=115200
board_name=EVK
board_rev=14X14
boot_fdt=try
bootcmd=run findfdt;mmc dev ${mmcdev};mmc dev ${mmcdev}; if mmc rescan; then if run loadbootscript; then run bootscript; else if run loadimage; then run mmcboot; else run netboot; fi; fi; else run netboot; fi
bootcmd_mfg=run mfgtool_args;bootz ${loadaddr} ${initrd_addr} ${fdt_addr};
bootdelay=1
bootscript=echo Running bootscript from mmc ...; source
console=ttymxc0
ethact=FEC1
ethprime=FEC
fdt_addr=0x83000000
fdt_file=imx6ull-14x14-emmc-4.3-800x480-c.dtb
fdt_high=0xffffffff
findfdt=if test $fdt_file = undefined; then if test $board_name = EVK && test $board_rev = 9X9; then setenv fdt_file imx6ull-9x9-evk.dtb; fi; if test $board_name = EVK && test $board_rev = 14X14; then setenv fdt_file imx6ull-14x14-evk.dtb; fi; if test $fdt_file = undefined; then echo WARNING: Could not determine dtb to use; fi; fi;
image=zImage
initrd_addr=0x83800000
initrd_high=0xffffffff
ip_dyn=yes
loadaddr=0x80800000
loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};
loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}
loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}
logo_file=alientek.bmp
mfgtool_args=setenv bootargs console=${console},${baudrate} rdinit=/linuxrc g_mass_storage.stall=0 g_mass_storage.removable=1 g_mass_storage.file=/fat g_mass_storage.ro=1 g_mass_storage.idVendor=0x066F g_mass_storage.idProduct=0x37FF g_mass_storage.iSerialNumber="" clk_ignore_unused
mmcargs=setenv bootargs console=${console},${baudrate} root=${mmcroot}
mmcautodetect=yes
mmcboot=echo Booting from mmc ...; run mmcargs; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if run loadfdt; then bootz ${loadaddr} - ${fdt_addr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi;
mmcdev=1
mmcpart=1
mmcroot=/dev/mmcblk1p2 rootwait rw
netargs=setenv bootargs console=${console},${baudrate} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp
netboot=echo Booting from net ...; run netargs; if test ${ip_dyn} = yes; then setenv get_cmd dhcp; else setenv get_cmd tftp; fi; ${get_cmd} ${image}; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if ${get_cmd} ${fdt_addr} ${fdt_file}; then bootz ${loadaddr} - ${fdt_addr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi;
panel=ATK-LCD-4.3-800x480
script=boot.scr
splashimage=0x88000000
splashpos=m,m
stderr=serial
stdin=serial
stdout=serial

Environment size: 2583/8188 bytes

uboot的存放位置:
SD卡是在第2扇区处。

sudo dd if=u-boot-imx6ull-14x14-ddr512-emmc.imx of=/dev/sdb bs=1024 seek=1 conv=fsy

eMMC是在boot0区的第2扇区处。

echo 0 > /sys/block/mmcblk1boot0/force
dd if=u-boot-imx6ull-14x14-ddr512-emmc.imx of=/dev/mmcblk1boot0 bs=1024 seek=1 conv=fsy

linux内核和设备树的存放位置:
以文件的形式存放在用户空间的第一个分区处,文件的扩展名是随便起的。分区的文件系统是FAT32,在uboot中用fatload命令读取到内存。

uboot环境变量中只有bootcmd,没有bootargs。
bootargs环境变量是通过执行run mmcargs命令动态生成的,断电后就消失。

bootcmd环境变量的内容:

run findfdt
mmc dev ${mmcdev}
mmc dev ${mmcdev}
if mmc rescan
then
	if run loadbootscript
	then
		run bootscript
	else
		if run loadimage
		then
			run mmcboot
		else
			run netboot
		fi
	fi
else
	run netboot
fi

findfdt环境变量的内容:

if test $fdt_file = undefined
then
	if test $board_name = EVK && test $board_rev = 9X9
	then
		setenv fdt_file imx6ull-9x9-evk.dtb
	fi
	if test $board_name = EVK && test $board_rev = 14X14
	then
		setenv fdt_file imx6ull-14x14-evk.dtb
	fi
	if test $fdt_file = undefined
	then
		echo WARNING: Could not determine dtb to use
	fi
fi

其中fdt_file=imx6ull-14x14-emmc-4.3-800x480-c.dtb。
if条件不成立,所以这里面不执行任何语句。

loadbootscript环境变量的内容:确定
fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script}
即fatload mmc 1:1 0x80800000 boot.scr
由于boot.scr文件不存在,所以这个命令会执行失败。

loadimage环境变量的内容:
fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}
即fatload mmc 1:1 0x80800000 zImage
因为zImage文件存在,所以这个命令能执行成功,将FAT32分区中的zImage文件的内容读到0x80800000内存地址处。

“if run loadimage”条件成立,执行mmcboot。
mmcboot的内容如下:

echo Booting from mmc ...
run mmcargs
if test ${boot_fdt} = yes || test ${boot_fdt} = try
then
	if run loadfdt
	then
		bootz ${loadaddr} - ${fdt_addr}
	else
		if test ${boot_fdt} = try
		then
			bootz
		else
			echo WARN: Cannot load the DT
		fi
	fi
else
	bootz
fi

mmcargs的内容是setenv bootargs console=${console},${baudrate} root=${mmcroot}
即setenv bootargs console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw
这个就是设置bootargs环境变量的值,断电后就会丢失。
接下来因为boot_fdt=try,所以if条件成立,执行loadfdt。
fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}
即fatload mmc 1:1 0x83000000 imx6ull-14x14-emmc-4.3-800x480-c.dtb
就是把设备树文件imx6ull-14x14-emmc-4.3-800x480-c.dtb读到0x83000000内存地址处。
读取成功,然后执行bootz ${loadaddr} - ${fdt_addr}启动linux内核(zImage)。
请注意这个横杠不是减号的意思,不是loadaddr地址减去fdt_addr地址。横杠仅仅表示参数2为空。
参数1是loadaddr,是linux内核在内存中的地址。
参数2为空,是ramdisk的内容在内存中地址。因为我们的根文件系统挂载的是eMMC分区,不是ramdisk镜像,所以参数2必须为空,用横杠表示。
参数3是fdt_addr,是设备树在内存中的地址。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值