一、概述:
linux启动分为,emmc,nand,spi norflash,sd卡启动,通过这些介质上烧录的镜像文件(uboot,kernel,fs,data)加载到ddr,启动linux操作系统。关于原理前面文章有详细说明。
之前一直没有明白,产品量产时,sd卡固化镜像的原理,以及uboot里为啥会有mtd,关于分区的代码。以及uboot给内核传参时,会有设置分区大小的命令,读取内核启动内核bootm地址是怎么来的。flash地址0x100000是内核分区的扇区地址,可以写成kernel,0x100000不好记,这个地址随自己实际情况定的。
下面四个设置,解释
1、
setenv bootcmd 'sf probe 0;sf read 0x82000000 0x100000 0x400000;bootm 0x82000000'
//sf probe 0;片选命令,这里选中第0片flash。
//sf read 0x82000000 0x100000 0x400000;从flash地址0x100000处读取长度为0x400000的内容,拷贝至内存的0x82000000地址。
flash地址0x100000是内核分区的扇区地址,可以写成kernel,0x100000不好记
2、
setenv bootargs 'mem=512M console=ttyAMA0,115200 rw rootwait root=/dev/mmcblk0p3 rootfstype=ext4 blkdevparts=mmcblk0:1M(boot),16M(kernel),512M(rootfs),28G(data)'
//rootwait root=/dev/mmcblk0p3 高数内核,rootfs在emmc0 0号设备,p3第3分区
//blkdevparts=mmcblk0:1M(boot),16M(kernel),512M(rootfs),28G(data),高数内核各个分区的大小,就可以得到偏移量,找到所有分区的位置
3、
setenv bootcmd 'tftp 0x44000000 uImage_hi3559av100_multi-core; bootm 0x44000000'
tftp下载内核到ddr,直接运行启动,不固化,这种是开发调试时用的
4、
mmcdev=1,mmcpart=1,
imx6ull-14x14-evk.dtb中,fdt_addr的定义所示:"fdt_addr=0x83000000\0"
loadfdt = fatload mmc 1:1 0x83000000 imx6ull-14x14-evk.dtb
因此loadfdt的作用就是从mmc1的分区1中读取imx6ull-14x14-evk.dtb文件到内存0x83000000处。
uboot启动后,从EMMC中读取zImage镜像文件和设备树文件,然后调用bootz启动Linux kernel
mmc dev 1 //切换到EMMC
fatload mmc 1:1 0x80800000 zImage //读取zImage到0x80800000处
fatload mmc 1:1 0x83000000 imx6ull-14x14-evk.dtb //读取设备树到0x83000000处
bootz 0x80800000 - 0x83000000
emmc分区如下
root用来设置根文件系统的位置,root=/dev/mmcblk1p2用于指明根文件系统存放在mmcblk1设备的分区2中。
假如有两个emmc设备0,1 分别有两个分区。/dev/mmcblk0p1、/dev/mmcblk0p2、/dev/mmcblk1p1 、/dev/mmcblk1p2
/dev/mmcblkx (x=0~n)表示 mmc设备,而/dev/mmcblkxp 表示mmc设备x的分区号。
/dev/mmcblk1表示EMMC,而/dev/mmcblk1p2表示 EMMC的分区 2。
举例:setenv bootargs 'mem=511M console=ttyAMA0,115200 root=/dev/mtdblock2 rootfstype=jffs2 mtdparts=hi_sfc:1M(boot),4M(kernel),11M(rootfs);hinand:436M(nand_ro),40M(nand_rw1),20M(nand_rw2) '
//解释
root=/dev/mtdblock2 ,root所在分区,对应下面的11M(rootfs)。hi_sfc:1M(boot),4M(kernel),11M(rootfs);nor flash分成3个区。这里我们把boot,kernel,rootfs镜像文件存储在nor flash的3个分区里。hinand:436M(nand_ro),40M(nand_rw1),20M(nand_rw2) ;nand flash分成3个用户区。
二、分区,本文介绍uboot分为手动,自动对emmc,nand分区
1、uboot环境下,通过命令fdisk,mount,手动对emmc,nand,分区,格式化,挂载,烧录镜像文件。
2、uboot源代码,自动调用命令,对emmc,nand,分区,格式化,挂载,烧录镜像文件。(这种方法适合sd卡固化量产程序)
uboot源代码是移植了kernel内核的驱动代码的,关于emmc,nand,是从内核移植过来的。将这些代码移植到uboot,就可以对emmc,nand,sd卡操作了,提供命令格式,也可以直接调用函数,自动分区。uboot启动后,将emmc分区并格式化,uboot下支持fat和ext2的格式,这里用fat格式。这样重启后,进入uboot下,将emmc中的文件load到内存中运行。
三、uboot下,手动对emmc分区,读写文件,烧录镜像文件:
uboot启动后,输入mmc info有mmc的相关信息,输入fdisk -l 能看到/dev/mmcblk0的信息,fdisk命令能对对mmc,nand分区,格式化。
fdisk命令是linux系统的,提供的驱动操作代码,uboot从kernel移植扣过来的,uboot下也是有drvier文件夹的,支持了emmc,nand,sd卡操作的接口。所以fdisk在linux,uboot都可以用。
1、mmc命令介绍
在设备进入u-boot 模式下,输入mmc,可以看到帮助信息,支持的命令:
hisilicon # mmc
有下面这些命令
Usage:
mmc info - display info of the current MMC device
mmc reg [dev] - display register of the current MMC device
mmc read dev addr blk# cnt
mmc write dev addr blk# cnt
mmc erase blk# cnt
mmc rescan
mmc part - lists available partition on current mmc device
mmc dev [dev] [part] - show or set current mmc device [partition]
mmc list - lists available devices
mmc hwpartition [args...] - does hardware partitioning
支持的命令
1:对mmc读操作
mmc read addr blk cnt
2:对mmc写操作
mmc write addr blk cnt
3:对mmc擦除操作
mmc erase blk# cnt
4:重新搜索mmc设备
mmc rescan
5:列出mmc的分区
mmc part - lists available partition oncurrent mmc device
6:查看当前的设备号,或者设置设备号及分区
mmc dev [dev] [part] - show or set currentmmc device [partition]
hisilicon # mmc dev 0
First descriptor is NOT a primary desc on 0:1
switch to partitions #0, OK
mmc0(part 0) is current device
7:显示boot分区号
mmc bootpart [dev] [part] - show or setboot partition
mmc bootpart
显示如下:
Device 3: boot partition 1 is for boot
分析:uboot处于第一个分区
8:列出当前的mmc设备
mmc list - lists available devices
hisilicon # mmc list
hisi-sdhci: 0 (eMMC)
9: 打印一些当前mmc设备的信息
输入:mmcinfo
显示结果:
Device: hisi-sdhci
Manufacturer ID: 45
OEM: 100
Name: SDW32
Tran Speed: 200000000
Rd Block Len: 512
MMC version 5.0
High Capacity: Yes
Capacity: 31268536320
Bus Width: 8-bit DDR
Erase Group Size: 512 KiB
HC WP Group Size: 8 MiB
User Capacity: 29.1 GiB WRREL
Boot Capacity: 4 MiB ENH
RPMB Capacity: 4 MiB ENH
10、mmc list
显示如下:
FSL_USDHC: 0
FSL_USDHC: 1
FSL_USDHC: 2
FSL_USDHC: 3
分析:boot中配置了四个mmc资源,SD卡是2,EMMC是3
11、切换设备
mmc dev 3
显示如下:
mmc3(part 0) is current device
分析:表示当前处于EMMC设备的第0个分区。
注意:当你想从eMMC读取内容的时候,你需要先确定目标内容在哪一个分区。否则读出的内容是错误的。
12、切换某个设备的某个分区
mmc dev 3 1
显示如下:
mmc3(part 1) is current device
分析:设置当前处于EMMC3设备的第1个分区
切换到设备0的0分区
mmc dev 0 0
显示如下:
switch to partitions #0, OK
mmc0(part 0) is current device
分析:切换到mmc0设备的第0个分区
13、显示当前EMMC的所有分区
mmc part
显示如下:
Partition Map for UNKNOWN device 3 – Partition Type: DOS
Partition Start Sector Num Sectors Type
1 16384 16384 83
2 32768 16384 83
3 49152 6397952 5 Extd
4 6447104 9150464 83
5 49153 3145727 83
6 3194881 3145727 83
7 6340609 16383 83
8 6356993 8191 83
分析:显示当前EMMC的所有分区,有8个分区,有块地址,扇区地址,大小
2、读取mmc上的数据到内存
mmc read addr blk cnt
参数:
addr: 读取到内存的地址值
blk: 读取block位置,这个位置是mmc相对于0地址的偏移量,是16进制,block单位是512字节
cnt: 读取的block个数,要读取到内存的数据大小,是16进制
例:
mmc dev 3 0
mmc read 0x10800000 600 10
命令分析:表示从mmc上1536×512个字节开始处(1536是ox600的十进制),读取16×512个字节(16的16进制是为10)到内存0x10800000 处
显示结果:
MMC read: dev 3, block # 1536, count 16 … 16 blocks read: OK
结果分析:
我们只能从结果看出来读取成功,但不能确定读取的内容是否正确,所以打印下内存的数据,看是否与mmc中的内容一致
uboot下,内存的使用指令。读取内存,显示数据看是否与mmc中的内容一致
md.b 0x10800000 100
命令分析:打印内存位置0x10800000的100个字节
显示结果:
10800000: b15ecb3c 6f62 746f 6564 616c 3d79 0033 ^.<.bootdelay=3.
10800010: 61626475 6172 6574 313d 3531 3032 0030 baudrate=115200.
1000020: 70696461 7264 313d 3239 312e 3836 312e ipaddr=192.168.1
10800030: 312e3330 7300 7265 6576 6972 3d70 3931 .103.serverip=19
10800040: 2e323631 2e38 2e31 3031 0031 656e 6d74 2.168.1.101.netm
10800050: 73613d6b 3532 2e35 3532 2e35 3532 2e35 ask=255.255.255.
结果分析:
我在mmc位置1536×512的位置储存的是环境变量,与内存打印的一致,读取正确
3、将内存上的数据写入mmc中
mmc write addr blk cnt
参:
addr: 从内存读取的位置
blk: 写入到mmc中block位置,这个位置是mmc的0地址的偏移量,是16进制,block单位是512字节
cnt: 写入到mmc中block个数,要写入的数据大小,是16进制,block单位是512字节
例:
mmc dev 3
mmc write 0x108000000 0 100
命令分析:表示从内存0x108000000的位置上读取256*512的数据(256是100的十进制)到mmc上0的位置处,这里就不举例验证了
mmc上0的位置处是分区表,我把内存上的乱数据写进去后,uboot检测不到分区了
3、擦除指令
mmc erase blk cnt
参数:
blk: 擦除的mmc中block位置,这个位置是mmc的0地址的偏移量,是16进制,block单位是512字节
cnt: 擦除的mmc中block个数,是16进制,block单位是512字节
4、uboot下的,fdisk命令,对emmc,nand,分区,格式化等
fdisk /dev/mmcblk0
上面介绍mmc操作命令,也是可以新建分区的,我们也可以使用fdisk命令来,分区。
fdisk /dev/mmcblk0 是一个在 U-Boot 命令行环境下使用的命令,它用于对 /dev/mmcblk0 设备进行磁盘分区操作。
在 U-Boot 环境中直接运行 fdisk /dev/mmcblk0 会启动一个交互式的磁盘分区界面,允许你创建、删除、修改磁盘分区。
这里是如何在 U-Boot 环境中使用 fdisk 命令的步骤:
连接到目标设备的串行控制台。
重启目标设备进入 U-Boot 命令行模式。
在 U-Boot 提示符下输入 fdisk /dev/mmcblk0。
根据屏幕上显示的提示进行相应的磁盘分区操作。
注意:/dev/mmcblk0 是指你的 SD 卡或其他 MMC 设备。如果你的设备有不同的设备名称,请替换 /dev/mmcblk0
为正确的设备名称。在执行分区操作前,请确保你已经备份了所有重要数据。分区操作可能会导致数据丢失,请谨慎操作。
以下是一个简单的 U-Boot 命令行界面示例,展示了如何使用 fdisk 命令:
1、查看设备分区情况,输入fdisk -l /dev/mmcblk0
显示如下
Device: /dev/mmcblk0
Idx Cyls Sects Szi Type
0 + 0 0 0 0 Empty
1 255 63 8 0x0C Win95 FAT32 (LBA)
2 + 0 0 0 0 Empty
2、输入fdisk /dev/mmcblk0
显示如下
3、输入m,查看支持的命令格式,分别位a ,b ,c,d 代表什么操作,都列出来了
Command (m for help): m
显示如下
Command action
a toggle a bootable flag
b edit bsd disklabel
c toggle the dos compatibility flag
d delete a partition
l list known partition types
m print this menu
n add a new partition
o create a new empty DOS partition table
p print the partition table
q quit without saving changes
s create a new empty Sun disklabel
t change a partition’s system id
u change display/entry units
v verify the partition table
w write table to disk and exit
x extra functionality (experts only)
做如下实验
1、先P,看看sd卡的分区现状。
3、n,创建分区
1、p看sd卡的分区现状。
Command (m for help):p
显示如下
Partition number (1-4): 1
First cylinder (1-62528, default 1): 1
Last cylinder, +cylinders or +size{K,M,G} (1-62528, default 62528): +1500M
2、n创建分区
Command (m for help): n
显示如下
Command action
e extended
p primary partition (1-4)
3、t指定分区类型,
Command (m for help): t
显示如下
Selected partition 1
4、列出支持的文件系统格式
Hex code (type L to list codes): L
显示如下
0 Empty 24 NEC DOS 81 Minix / old Lin bf Solaris
1 FAT12 39 Plan 9 82 Linux swap / So c1 DRDOS/sec (FAT-
2 XENIX root 3c PartitionMagic 83 Linux c4 DRDOS/sec (FAT-
3 XENIX usr 40 Venix 80286 84 OS/2 hidden C: c6 DRDOS/sec (FAT-
4 FAT16 <32M 41 PPC PReP Boot 85 Linux extended c7 Syrinx
5 Extended 42 SFS 86 NTFS volume set da Non-FS data
6 FAT16 4d QNX4.x 87 NTFS volume set db CP/M / CTOS / .
7 HPFS/NTFS 4e QNX4.x 2nd part 88 Linux plaintext de Dell Utility
8 AIX 4f QNX4.x 3rd part 8e Linux LVM df BootIt
9 AIX bootable 50 OnTrack DM 93 Amoeba e1 DOS access
a OS/2 Boot Manag 51 OnTrack DM6 Aux 94 Amoeba BBT e3 DOS R/O
b W95 FAT32 52 CP/M 9f BSD/OS e4 SpeedStor
c W95 FAT32 (LBA) 53 OnTrack DM6 Aux a0 IBM Thinkpad hi eb BeOS fs
e W95 FAT16 (LBA) 54 OnTrackDM6 a5 FreeBSD ee GPT
f W95 Ext’d (LBA) 55 EZ-Drive a6 OpenBSD ef EFI (FAT-12/16/
10 OPUS 56 Golden Bow a7 NeXTSTEP f0 Linux/PA-RISC b
11 Hidden FAT12 5c Priam Edisk a8 Darwin UFS f1 SpeedStor
12 Compaq diagnost 61 SpeedStor a9 NetBSD f4 SpeedStor
14 Hidden FAT16 ❤️ 63 GNU HURD or Sys ab Darwin boot f2 DOS secondary
16 Hidden FAT16 64 Novell Netware af HFS / HFS+ fb VMware VMFS
17 Hidden HPFS/NTF 65 Novell Netware b7 BSDI fs fc VMware VMKCORE
18 AST SmartSleep 70 DiskSecure Mult b8 BSDI swap fd Linux raid auto
1b Hidden W95 FAT3 75 PC/IX bb Boot Wizard hid fe LANstep
1c Hidden W95 FAT3 80 Old Minix be Solaris boot ff BBT
1e Hidden W95 FAT1
5、选择6,类型的格式
Hex code (type L to list codes): 6
显示如下
Changed system type of partition 1 to 6 (FAT16)
这样就分好了第一个分区,并且指定了分区为fat16
6、如下p查看一下
Command (m for help): p
显示如下
Disk /dev/mmcblk0: 2048 MB, 2048917504 bytes
4 heads, 16 sectors/track, 62528 cylinders
Units = cylinders of 64 * 512 = 32768 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x6f20736b
Device Boot Start End Blocks Id System
/dev/mmcblk0p1 1 48001 1536024 6 FAT16
7、好了,把分区信息写入磁盘。
Command (m for help): w
显示如下
The partition table has been altered!
Calling ioctl() to re-read partition table.
WARNING: If you have created or modified any DOS 6.x
partitions, please see the fdisk manual page for additional
information.
Syncing disks.
**5、对分区格式化为fat16 的命令 mkdosfs **
输入命令,mkdosfs /dev/mmcblk0p1,上面其实有格式化的选项,但是也可以用这个命令格式化
看一下格式化后的
fdisk -l
显示如下
Disk /dev/mmcblk0: 1920 MB, 1920991232 bytes
4 heads, 16 sectors/track, 58624 cylinders
Units = cylinders of 64 * 512 = 32768 bytes
Device Boot Start End Blocks Id System
/dev/mmcblk0p1 1 58624 1875960 6 FAT16
分区完成后,就可以对指定分区烧录uboot,kernel,fs,data分区了。这里不演示了,别的文章,有说如何写nand,emmc文件的。
6、linux下,把emmc分区挂载在一目录下,就可以访问到这个分区了
linux系统后,就可以挂载分区了,当然kernel启动是不需要挂载分区的,uboot启动后,会读取emmc的kernel分区,镜像文件,到ddr,并告诉kernel,ddr启动地址,然后解压启动。kernel启动完后,最后挂载分区时,是需要mount分区的,才能访问到分区文件。 uboot root=/dev/mmcblk0p3 rootfstype=ext4,告诉内核rootfs在哪个分区,什么类型。
如下mount /dev/mmcblk0p1 /vm1这种,mmcblk0p1 是没有烧录镜像的,否则挂载写了文件,保存了会破坏掉分区uboot的,如果不是玩,是不允许这么玩的,kernel启动后,应该也是不允许这么挂载uboot分区,mmcblk0p1应该是uboot分区。
mount /dev/mmcblk0p1 /vm1
这样就可以将文件拷贝到/vm下,这里新建了一txt文件,并输入内容,重启后在uboot下查看emmc中是否有改文件。
~ # cd vm
/vm # ls
/vm # vi a.txt
往a.txt文件,写内容
11111111111111111111111111111111111111111111
0000000000000000000000000000000000000000000
11111111111111111111111111111111111111111111
00000000000000000000000000000000000000000000
7、reboot重启后进入uboot下,查看mmc设备的文件
mmc list
Octeon MMC/SD0: 0
用fatls命令查看mmc下的文件信息,可以看到有linux下建的a.txt文件
fatls mmc 0
181 a.txt
1 file(s), 0 dir(s)
fatload 将mmc中a.txt load到内存中
fatload mmc 0 30000000 a.txt
显示如下
reading a.txt
181 bytes read
查看内存信息
md 30000000 //查看内存3000000地址信息。
显示结果如下
30000000: 31313131 31313131 31313131 31313131
30000010: 31313131 31313131 31313131 31313131 1111111111111111
30000020: 31313131 31313131 31313131 0a303030 111111111111.000
30000030: 30303030 30303030 30303030 30303030 0000000000000000
30000040: 30303030 30303030 30303030 30303030 0000000000000000
30000050: 30303030 30303030 0a313131 31313131 00000000.1111111
30000060: 31313131 31313131 31313131 31313131 1111111111111111
30000070: 31313131 31313131 31313131 31313131 1111111111111111
30000080: 31313131 310a3030 30303030 30303030 11111.0000000000
30000090: 30303030 30303030 30303030 30303030 0000000000000000
300000a0: 30303030 30303030 30303030 30303030 0000000000000000
300000b0: 30300a0a 0a000000 00000000 00000000 00…
300000c0: 00000000 00000000 00000000 00000000 …
300000d0: 00000000 00000000 00000000 00000000 …
300000e0: 00000000 00000000 00000000 00000000 …
300000f0: 00000000 00000000 00000000 00000000 …
可以看到就是a.txt的内容,说明emmc内容写入成功。
三、uboot下,自动对emmc分区,读写文件,烧录镜像文件:
所以是不是自动分区,就是写死在uboot里,mtdparts,uboot在ddr起来后,就可以根据这份表,自动进行分区,并读取外插入的sd卡,烧录uboot,kernel,fs到对应的分区。我有一篇文章写了linux,python写烧录分区的,是根据一份dts配置表生产的,根据这个文件,来分区,并烧录的,这篇文章不公开。
3、无设备树内核情况
1、内核分区表
在kernel源码的arch/arm/mach-s3c2440/mach-mini2440.c里有如下flash分区配置表:
U-Boot分区的实现细节
实现U-Boot分区功能主要通过run_command函数和mtdparts命令来完成。首先,通过初始化环境变量和
解析mtdparts命令来实现分区的具体配置。mtdparts在uboot源码里可以写死在bootarg,烧录固化时,
根据这个来分区固化,不是bootarg传参得来的。
例如
执行run_command("mtdparts default", 0)
可以实现分区功能,这个命令位于Cmd_jffs2.c文件中。通过这种方式,U-Boot能够有效地管理存储设备的不同分区。
要写把镜像文件读取到ddr、有可能是usb写到ddr,如果是sd卡固化,就是去读取sd卡的分区镜像文件。如果是开发板,调试
通过usb,或网线,tftp的方式下载的。这种不能大批量生产。
然后就可以这样调命令,自动写了
nand erase bootloader //擦除bootloader分区
nand write 30000000 bootloader
tftp 30000000 uImage //学习调试时,tftp网线下载到ddr,下载kernel uImage到ddr
nand erase kernel //擦除kernel分区
nand write 30000000 kernel
若分区没有成功,可能导致内核启动失败。这可能是内核中的相应驱动没有去解析这个设备树的分区导致的。
原来的代码
mtd_device_parse_register(mtd, NULL, 0, NULL, 0);
修改后的代码
static const char *part_probe_types[]
= { "cmdlinepart", "ofpart", "ofoldpart", NULL };
struct mtd_part_parser_data ppdata;
if (pdev->dev.of_node)
ppdata.of_node = pdev->dev.of_node;
mtd_device_parse_register(mtd, part_probe_types, &ppdata, NULL, 0);
如上面dts分区配置表,我多篇文章提到,新版本内核,有dts,分区配置表,根据这份表,指导内核进行启动,同时,会生出一个文件,指导python脚本写的工具,通过usb完成烧录。别的芯片商怎么做我不知道,我做的项目只看到配置这样一份文件,应该也会搞到uboot里去,uboot启动时,也会根据这份文件,来调用上面mmc分区指令,或代码函数,来完成分区,烧录的。dts代替写死在uboot代码,现在新版本配置相关应该都是dts文件。我项目开发的是时候,芯片商提供的uboot,bootargs没有前面那样传各个分区的地址,大小,应该是dts这种方式,来分区的。
最后总结
最后总结
sd卡固化程序,烧录镜像,应该是在uboot写死了,然后读取uboot到ddr运行,uboot根据这份表,调用相关指令来,来进行分区的,自动调用nand wrie mmc wrire写入nand,emmc的。
具体操作不想深究,没意思,我又不搞这玩意,还是我前面写的人生感悟,我的目的是攥钱,不是为了好玩,实际上不好玩,也许这辈子,都不会去芯片公司,写这种动,知道点皮毛究可以了。
还有一种是芯片商,提供的python脚本,linux下,make creat bsp,就会烧录程序,文中有说到原理。大概就是那么回事。也不管了。