以下内容源于网络资源的学习与整理,如有侵权请告知删除。
参考内容
硬盘分区、寻址和系统启动过程 - 走看看
x210:iNand分区情况_毛裤先生_2的博客-CSDN博客
Linux系统是如何识别硬盘设备和硬盘分区的?
uboot之uboot中环境变量_雨于鱼的博客-CSDN博客_uboot环境变量
扇区与块(sectors,block)_terryliu98的博客-CSDN博客_扇区和块
关于uboot代码所设置的raw分区分析_chegxy的博客-CSDN博客
uboot 下更改INAND的分区 fdisk_疯仔嵌入式的博客-CSDN博客
SD卡烧写Linux kernel——SD卡分区,并烧写uboot,kernel,DTB及filesystem_HelloTonyGo的博客-CSDN博客
X210开发板(S5PV210芯片)uboot中SD卡分区分析(init_raw_area_table函数)
S5PV210 Uboot开发与移植01:Uboot概述_麦兜的学习笔记的博客-CSDN博客
目录
一、前言
二、fdisk命令的代码分析
1、命令的定义
2、 do_fdisk函数
3、create_mmc_fdisk函数
4、make_mmc_partition函数
5、calc_unit函数
6、make_partitionInfo函数
7、encode_partitionInfo函数
8、put_mmc_mbr函数
9、print_mmc_part_info函数
10、get_mmc_part_info函数
三、总结
一、前言
(1)在uboot的控制台中,使用“mm list”命令得知X210开发板的iNand对应的设备号为0,因此可以使用“fdisk -c 0”命令对X210开发板的iNand进行分区,可以使用“fdisk -p 0”命令打印这个iNand的分区情况。
x210 # mmc list //列出开发板接有哪些mmc设备 S3C_HSMMC0_dev0 S3C_HSMMC2_dev1 x210 # fdisk -p 0 //查看编号为0的设备(即iNand)的分区情况partion # size(MB) block start # block count partition_Id 1 258 22374 529518 0x83 2 120 551892 246114 0x83 3 101 798006 208824 0x83 4 3222 1006830 6600330 0x83 x210 # fdisk -p 1 //查看编号为1(即sd卡)的分区情况partion # size(MB) block start # block count partition_Id 1 7766 31248 15905232 0x0C x210 # fdisk -c 0 //再次对iNand进行分区 fdisk is completedpartion # size(MB) block start # block count partition_Id 1 258 22374 529518 0x83 2 120 551892 246114 0x83 3 101 798006 208824 0x83 4 3222 1006830 6600330 0x83 x210 #
(2)uboot源码中使用一个函数实现一个命令,fdisk这个命令也是如此。 下面将对fdisk这个命令所对应的函数进行分析。
二、fdisk命令的代码分析
1、命令的定义
(1)fdisk命令定义在/common/cmd_mmc_fdisk.c文件中,对应着do_fdisk函数。
U_BOOT_CMD(fdisk, 3, 0, do_fdisk,"fdisk\t- fdisk for sd/mmc.\n","-c <device_num>\t- create partition.\n""fdisk -p <device_num>\t- print partition information\n" );
(2) U_BOOT_CMD的分析见博客uboot将命令结构体单独存放在某个代码段的方法。
(3)跟踪do_fdisk函数。
- ……do_fdisk //根据参数选择执行其中一个(加粗显示的)函数
- ………create_mmc_fdisk //对iNand进行分区(将mbr[ ]数组写入iNand的0扇区)
- ………………get_mmc_block_count //获取iNand一共有多少个扇区
- ………………make_mmc_partition //对构造与填充mbr[ ]数组
- ……………………calc_unit //计算某个区间共有多少个扇区
- ……………………make_partitionInfo //构造每个分区的信息(16字节)
- ……………………encode_partitionInfo //用分区的信息填充mbr[ ]数组
- ………………put_mmc_mbr //将mbr[ ]数组写入iNand的0扇区
- ………print_mmc_part_info //打印分区信息
- ………………get_mmc_part_info //获取分区信息
2、 do_fdisk函数
(1)函数位置与内容
该函数定义在/common/cmd_mmc_fdisk.c文件中,内容如下:
int do_fdisk(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) {if ( argc != 3 ){printf("Usage: fdisk <-c or -p> <device_num>\n");return 0;}if ( strcmp(argv[1], "-c") == 0 )return create_mmc_fdisk(argc, argv);else if ( strcmp(argv[1], "-p") == 0 )return print_mmc_part_info(argc, argv);printf("Usage: fdisk <-c or -p> <device_num>\n");return 0; }
(2)函数的主要工作
当uboot控制台中执行“fdisk -c 0”时,执行create_mmc_fdisk函数。
当uboot控制台中执行“fdisk -p 0”时,执行print_mmc_part_info函数。
3、create_mmc_fdisk函数
(1)函数位置与内容
该函数定义在/common/cmd_mmc_fdisk.c文件中,内容如下:
int create_mmc_fdisk(int argc, char *argv[]) {int rv;int total_block_count;unsigned char mbr[512];memset(mbr, 0x00, 512); //获得块信息,以512字节为单位total_block_count = get_mmc_block_count(argv[2]); if (total_block_count < 0)return -1;//对iNand分区(其实只是在构造与填充mbr[]这个数组内容)make_mmc_partition(total_block_count, mbr, (argc==6?1:0), argv);//对iNand分区rv = put_mmc_mbr(mbr, argv[2]);if (rv != 0)return -1;printf("fdisk is completed\n");//分区成功,打印信息argv[1][1] = 'p';print_mmc_part_info(argc, argv);//和fdisk –p 0 作用一样,打印出分区信息return 0; }
(2)函数的主要工作
首先,调用get_mmc_block_count函数获取iNand的扇区数目。
然后,调用make_mmc_partition函数构造mbr[]数组。
接着,调用put_mmc_mbr函数将mbr[ ]信息写入iNand的0扇区。
最后,调用print_mmc_part_info函数打印分区信息。
4、make_mmc_partition函数
(1)函数位置与内容
该函数在/common/cmd_mmc_fdisk.c文件中,内容如下:
int make_mmc_partition(int total_block_count, unsigned char *mbr) {int block_start = 0, block_offset;SDInfo sdInfo;PartitionInfo partInfo[4];memset((unsigned char *)&sdInfo, 0x00, sizeof(SDInfo));get_SDInfo(total_block_count, &sdInfo);///block_start = calc_unit(_10MB, sdInfo);block_offset = calc_unit(CONFIG_PART_SIZE, sdInfo);partInfo[0].bootable = 0x00;partInfo[0].partitionId = 0x83;make_partitionInfo(block_start, block_offset, sdInfo, &partInfo[0]);///block_start += block_offset;block_offset = calc_unit(SYSTEM_PART_SIZE, sdInfo);partInfo[1].bootable = 0x00;partInfo[1].partitionId = 0x83;make_partitionInfo(block_start, block_offset, sdInfo, &partInfo[1]);/// block_start += block_offset;block_offset = calc_unit(CACHE_PART_SIZE, sdInfo);partInfo[2].bootable = 0x00;partInfo[2].partitionId = 0x83;make_partitionInfo(block_start, block_offset, sdInfo, &partInfo[2]);/// block_start += block_offset;block_offset = BLOCK_END;partInfo[3].bootable = 0x00;partInfo[3].partitionId = 0x83;make_partitionInfo(block_start, block_offset, sdInfo, &partInfo[3]);/// memset(mbr, 0x00, sizeof(mbr));mbr[510] = 0x55; mbr[511] = 0xAA;encode_partitionInfo(partInfo[0], &mbr[0x1BE]);encode_partitionInfo(partInfo[1], &mbr[0x1CE]);encode_partitionInfo(partInfo[2], &mbr[0x1DE]);encode_partitionInfo(partInfo[3], &mbr[0x1EE]);return 0; }
(2)函数的主要工作
首先,调用calc_unit函数计算每个分区(共4个分区)的起始扇区与扇区数目。
然后,以calc_unit函数计算结果作为参数,调用make_partitionInfo函数构造每个分区的信息(就是MBR分区表的简介中写到的16字节的内容)。
最后,调用encode_partitionInfo函数,将4个分区的信息分别写到mbr[ ]数组中,也就是mbr[446]~mbr[461]、mbr[462]~mbr[477]、mbr[478]~mbr[493]、mbr[494]~mbr[509]中。
将来,put_mmc_mbr函数负责将mbr[ ]数组写进iNand的0扇区(主引导扇区),这样就完成了所谓的分区。
(3)函数的细节分析
1)宏与分区
调用calc_unit函数时传递了几个宏,这些宏决定着分区的大小。
block_start = calc_unit(_10MB, sdInfo); //10MB block_offset = calc_unit(CONFIG_PART_SIZE, sdInfo);//256MB block_offset = calc_unit(SYSTEM_PART_SIZE, sdInfo);//120MB block_offset = calc_unit(CACHE_PART_SIZE, sdInfo);//100MB block_offset = BLOCK_END;//3GB
#define BLOCK_SIZE 512 #define BLOCK_END 0xFFFFFFFF #define _10MB (10*1024*1024) #define _100MB (100*1024*1024) #define _8_4GB (1023*254*63)#ifdef CONFIG_S5PC110 #define SYSTEM_PART_SIZE (120*1024*1024) #else #define SYSTEM_PART_SIZE (384*1024*1024) #endif#define USER_DATA_PART_SIZE (1024*1024*1024) #define CACHE_PART_SIZE _100MB #define CONFIG_PART_SIZE (256*1024*1024)#define CHS_MODE 0 #define LBA_MODE !(CHS_MODE)
根据上面的宏,uboot给iNand分区的情况如下:
分区大小 分区用途 10MB 空出iNand的前10MB空间(这部分有MBR、uboot的BL1与BL2、Linux kernel) 256MB CONFIG_PART_SIZE(p1分区) 120MB SYSTEM_PART_SIZE(p2分区) 100MB CACHE_PART_SIZE(p3分区) 剩余空间 用户空间(p4分区) 注意,这里的uboot源码并未同步官方镜像对分区表大小的修改。
官方镜像文件中,SYSTEM_PART_SIZE是256MB,表明p2分区大小是256MB。
这里的uboot源码中,SYSTEM_PART_SIZE是120MB,表明p2分区大小是120MB。
官方文件系统镜像rootfs_qt4.ext3有200多MB,如果使用这里的uboot源码编译生成的uboot.bin,在利用fastboot进行烧写时会提示文件太大写不进分区。
5、calc_unit函数
(1)函数位置与内容
该函数在/common/cmd_mmc_fdisk.c文件中,内容如下:
int calc_unit(int length, SDInfo sdInfo) {if (sdInfo.addr_mode == CHS_MODE)return ( (length / BLOCK_SIZE / sdInfo.unit + 1 ) * sdInfo.unit);elsereturn ( (length / BLOCK_SIZE) ); }
(2)函数的细节分析
1)LBA_MODE、CHS_MODE是指块设备的扇区定位方式,具体介绍见博客硬盘分区、寻址和系统启动过程。
2)在get_SDInfo函数中有如下代码:
if(block_count >= _8_4GB)sdInfo->addr_mode = LBA_MODE; elsesdInfo->addr_mode = CHS_MODE;
这里的“_8_4GB”是一个宏定义,值为(1023*254*63),表示7GB。因为X210开发板的iNand小于7G,故属于CHS_MODE。
3)在get_SDInfo函数中有如下代码:
if (sdInfo->addr_mode == CHS_MODE) {diff_min = C_max;for (H = H_start; H <= H_max; H++)for (S = S_start; S <= S_max; S++){C = block_count / (H * S);if ( (C <= C_max) ){diff = C_max - C;if (diff <= diff_min){diff_min = diff;sdInfo->C_end = C;sdInfo->H_end = H;sdInfo->S_end = S;}}} } else //省略一部分代码sdInfo->unit = sdInfo->H_end * sdInfo->S_end;
根据推导得到sdInfo.unit大小为7458。
4)第一个分区的开始地址(以扇区为单位),是以_10MB(10*1024*1024)作为calc_unit函数的参数返回的值。10*1024*1024/512/7458=2(忽略小数),(2+1)*7458=22374,这与利用“fdisk -p 0”打印的信息一致。其他分区是紧接着上一个分区的。
6、make_partitionInfo函数
(1)函数位置与内容
该函数在/common/cmd_mmc_fdisk.c文件中,内容如下:
void make_partitionInfo(int LBA_start, int count, SDInfo sdInfo, PartitionInfo *partInfo) {int temp = 0;int _10MB_unit;partInfo->block_start = LBA_start;//-----------------------------------------------------if (sdInfo.addr_mode == CHS_MODE){partInfo->C_start = partInfo->block_start / (sdInfo.H_end * sdInfo.S_end);temp = partInfo->block_start % (sdInfo.H_end * sdInfo.S_end);partInfo->H_start = temp / sdInfo.S_end;partInfo->S_start = temp % sdInfo.S_end + 1;if (count == BLOCK_END){_10MB_unit = calc_unit(_10MB, sdInfo);partInfo->block_end = sdInfo.C_end * sdInfo.H_end * sdInfo.S_end - _10MB_unit - 1;partInfo->block_count = partInfo->block_end - partInfo->block_start + 1;partInfo->C_end = partInfo->block_end / sdInfo.unit;partInfo->H_end = sdInfo.H_end - 1;partInfo->S_end = sdInfo.S_end;}else{partInfo->block_count = count;partInfo->block_end = partInfo->block_start + count - 1;partInfo->C_end = partInfo->block_end / sdInfo.unit;temp = partInfo->block_end % sdInfo.unit;partInfo->H_end = temp / sdInfo.S_end;partInfo->S_end = temp % sdInfo.S_end + 1;}} //-----------------------------------------------------else{partInfo->C_start = 0;partInfo->H_start = 1;partInfo->S_start = 1;partInfo->C_end = 1023;partInfo->H_end = 254;partInfo->S_end = 63;if (count == BLOCK_END){_10MB_unit = calc_unit(_10MB, sdInfo);partInfo->block_end = sdInfo.total_block_count - _10MB_unit - 1;partInfo->block_count = partInfo->block_end - partInfo->block_start + 1;}else{partInfo->block_count = count;partInfo->block_end = partInfo->block_start + count - 1;}} }
(2)函数的主要工作
这个函数其实就是根据传进来的参数,确定这个分区的起始扇区(磁头号、扇区号、柱面号)与结束扇区(磁头号、扇区号、柱面号)。
关于这部分的背景知识,见彻底搞懂硬盘相关的概念_天糊土的博客-CSDN博客,MBR分区表的简介_天糊土的博客-CSDN博客。
7、encode_partitionInfo函数
(1)函数位置与内容
该函数在/common/cmd_mmc_fdisk.c文件中,内容如下:
void encode_partitionInfo(PartitionInfo partInfo, unsigned char *result) {*result++ = partInfo.bootable;encode_chs(partInfo.C_start, partInfo.H_start, partInfo.S_start, result);result +=3;*result++ = partInfo.partitionId;encode_chs(partInfo.C_end, partInfo.H_end, partInfo.S_end, result);result += 3;memcpy(result, (unsigned char *)&(partInfo.block_start), 4);result += 4; memcpy(result, (unsigned char *)&(partInfo.block_count), 4); }void encode_chs(int C, int H, int S, unsigned char *result) {*result++ = (unsigned char) H;*result++ = (unsigned char) ( S + ((C & 0x00000300) >> 2) );*result = (unsigned char) (C & 0x000000FF); }
(2)函数的细节分析
该函数主要是将每个分区的信息转化为16字节的内容?(待求证)
8、put_mmc_mbr函数
(1)函数位置与内容
该函数在/common/cmd_mmc_fdisk.c文件中,内容如下:
int put_mmc_mbr(unsigned char *mbr, char *device_name) {int rv;struct mmc *mmc;int dev_num;dev_num = simple_strtoul(device_name, NULL, 0);mmc = find_mmc_device(dev_num);if (!mmc){printf("mmc/sd device is NOT founded.\n");return -1;} rv = mmc_init(mmc);if (rv){printf("mmc/sd device's initialization is failed.\n");return -1;}rv = mmc->block_dev.block_write(dev_num, 0, 1, mbr);if(rv == 1)return 0;elsereturn -1;
(2)函数的细节分析
首先,调用find_mmc_device函数寻找编号对应的存储设备(这里是iNand)。
然后,调用mmc_init函数初始化这个存储设备。
最后,调用struct mmc结构体成员中的block_write函数将mbr数组写入存储设备中。
9、print_mmc_part_info函数
(1)函数位置与内容
该函数在/common/cmd_mmc_fdisk.c文件中,内容如下:
int print_mmc_part_info(int argc, char *argv[]) {int rv;PartitionInfo partInfo[4];rv = get_mmc_part_info(argv[2], 1, &(partInfo[0].block_start), &(partInfo[0].block_count),&(partInfo[0].partitionId) );rv = get_mmc_part_info(argv[2], 2, &(partInfo[1].block_start), &(partInfo[1].block_count),&(partInfo[1].partitionId) );rv = get_mmc_part_info(argv[2], 3, &(partInfo[2].block_start), &(partInfo[2].block_count),&(partInfo[2].partitionId) );rv = get_mmc_part_info(argv[2], 4, &(partInfo[3].block_start), &(partInfo[3].block_count),&(partInfo[3].partitionId) );printf("\n"); printf("partion # size(MB) block start # block count partition_Id \n");if ( (partInfo[0].block_start !=0) && (partInfo[0].block_count != 0) ) printf(" 1 %6d %8d %8d 0x%.2X \n",(partInfo[0].block_count / 2048), partInfo[0].block_start,partInfo[0].block_count, partInfo[0].partitionId);if ( (partInfo[1].block_start !=0) && (partInfo[1].block_count != 0) ) printf(" 2 %6d %8d %8d 0x%.2X \n",(partInfo[1].block_count / 2048), partInfo[1].block_start,partInfo[1].block_count, partInfo[1].partitionId);if ( (partInfo[2].block_start !=0) && (partInfo[2].block_count != 0) ) printf(" 3 %6d %8d %8d 0x%.2X \n",(partInfo[2].block_count / 2048), partInfo[2].block_start,partInfo[2].block_count, partInfo[2].partitionId);if ( (partInfo[3].block_start !=0) && (partInfo[3].block_count != 0) ) printf(" 4 %6d %8d %8d 0x%.2X \n",(partInfo[3].block_count / 2048), partInfo[3].block_start,partInfo[3].block_count, partInfo[3].partitionId);return 1; }
(2)函数的细节分析
暂略。
10、get_mmc_part_info函数
(1)函数位置与内容
该函数在/common/cmd_mmc_fdisk.c文件中,内容如下:
int get_mmc_part_info(char *device_name, int part_num, int *block_start, int *block_count, unsigned char *part_Id) {int rv;PartitionInfo partInfo;unsigned char mbr[512];rv = get_mmc_mbr(device_name, mbr);if(rv !=0)return -1;switch(part_num){case 1:decode_partitionInfo(&mbr[0x1BE], &partInfo);*block_start = partInfo.block_start; *block_count = partInfo.block_count; *part_Id = partInfo.partitionId; break;case 2:decode_partitionInfo(&mbr[0x1CE], &partInfo);*block_start = partInfo.block_start; *block_count = partInfo.block_count; *part_Id = partInfo.partitionId; break;case 3:decode_partitionInfo(&mbr[0x1DE], &partInfo);*block_start = partInfo.block_start; *block_count = partInfo.block_count; *part_Id = partInfo.partitionId; break;case 4:decode_partitionInfo(&mbr[0x1EE], &partInfo);*block_start = partInfo.block_start; *block_count = partInfo.block_count; *part_Id = partInfo.partitionId; break;default:return -1;} return 0; }int get_mmc_mbr(char *device_name, unsigned char *mbr) {int rv;struct mmc *mmc;int dev_num;dev_num = simple_strtoul(device_name, NULL, 0);mmc = find_mmc_device(dev_num);if (!mmc){printf("mmc/sd device is NOT founded.\n");return -1;} rv = mmc_init(mmc);if (rv){printf("mmc/sd device's initialization is failed.\n");return -1;}rv = mmc->block_dev.block_read(dev_num, 0, 1, mbr);if(rv == 1)return 0;elsereturn -1; }void decode_partitionInfo(unsigned char *in, PartitionInfo *partInfo) {partInfo->bootable = *in;partInfo->partitionId = *(in + 4); memcpy((unsigned char *)&(partInfo->block_start), (in + 8), 4);memcpy((unsigned char *)&(partInfo->block_count), (in +12), 4); }
(2)函数的细节分析
暂略。
三、总结
经过上述内容,一个mbr数组就写进了iNand的0扇区(主引导扇区),也就完成了对iNand设备的分区。不过这里的分区是狭义上的分区,指的是将mbr数组写入主引导扇区。广义的分区还包括对分区进行格式化。由上面代码可知,似乎还没有进行格式化。
后续将简单分析find_mmc_device、mmc_init、struct mmc函数内容。
补充说明
(1)SD/iNand是由一个一个的扇区组成的,回忆裸机中讲到的210的启动时,BL1在SD卡的1扇区开始往后存放,SD卡的0扇区是不用的,因为它要用来放置MBR。
(2)MBR用来描述块设备的分区信息。事先定义了一个通用的数据结构来描述块设备的分区,我们只要按照这个标准将分区信息写入MBR中即可对该设备完成分区。MBR默认就是在块设备的第0个扇区上存放的。
(3)在uboot和内核之间iNand设备的分区信息是靠iNand自己传递的,所以uboot不用给内核传参时传递分区表信息。如果开发板用的是nandFlash的话,分区表一般是在内核中自己用代码构建的。所以nand版本的内核移植的时候一般都需要去移植更改nand分区表。