取自uboot中的程序,将sd卡前10MB空间留做启动分区,不影响后续存放其他文件:
/*
* Copyright (c) 2010 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BLOCK_SIZE 512 //一个块512字节
#define BLOCK_END 0xFFFFFFFF
#define _10MB (10*1024*1024)
#define _100MB (100*1024*1024)
#define _8_4GB (1023*254*63) //8.4G CHS模式的最大扇区数 c<1024 h<255 s<64
#define CHS_MODE 0
#define LBA_MODE !(CHS_MODE)
typedef struct
{
int C_start;
int H_start;
int S_start;
int C_end;
int H_end;
int S_end;
int available_block;//可用的扇区
int unit;
int total_block_count;//总共的扇区
int addr_mode; // LBA_MODE or CHS_MODE 寻址模式
} SDInfo;
typedef struct
{
unsigned char bootable;
unsigned char partitionId;
int C_start;
int H_start;
int S_start;
int C_end;
int H_end;
int S_end;
int block_start;
int block_count;
int block_end;
} PartitionInfo;
/
int calc_unit(int length, SDInfo sdInfo)
{
if (sdInfo.addr_mode == CHS_MODE)
return ( (length / BLOCK_SIZE / sdInfo.unit + 1 ) * sdInfo.unit);//length/BLOCK_SIZE+sdinfo.unit
else
return ( (length / BLOCK_SIZE) );//返回LBA模式下的扇区数目,和传入的参数无关
}
/写MBR中的分区chs数据
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);
}
/
void encode_partitionInfo(PartitionInfo partInfo, unsigned char *result)
{
*result++ = partInfo.bootable;//0x1be位置
encode_chs(partInfo.C_start, partInfo.H_start, partInfo.S_start, result);
result +=3;//指针在函数体内并没有变化,变化的只是副本和指向相同地址的数据,所以还要加偏移量
*result++ = partInfo.partitionId;//0x1c2位置的数据
encode_chs(partInfo.C_end, partInfo.H_end, partInfo.S_end, result);
result += 3;
*((int *)result) = partInfo.block_start;
result += 4;
*((int *)result) = partInfo.block_count;
}
/
void get_SDInfo(int block_count, SDInfo *sdInfo)
{
int C, H, S;
int C_max = 1023, H_max = 255, S_max = 63;
int H_start = 1, S_start = 1;
int diff_min = 0, diff = 0;
if(block_count >= _8_4GB)
sdInfo->addr_mode = LBA_MODE;//扇区数目大于CHS模式能够寻址的最大扇区数时,选择LBA寻址模式
else
sdInfo->addr_mode = CHS_MODE;//否则还是选择CHS寻址
//-----------------------------------------------------
if (sdInfo->addr_mode == CHS_MODE)//CHS模式下的扇区参数
{
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//LBA模式下的CHS最大参数
{
sdInfo->C_end = 1023;
sdInfo->H_end = 254;
sdInfo->S_end = 63;
}
//-----------------------------------------------------
sdInfo->C_start = 0;
sdInfo->H_start = 1;
sdInfo->S_start = 1;
sdInfo->total_block_count = block_count;
sdInfo->available_block = sdInfo->C_end * sdInfo->H_end * sdInfo->S_end;
sdInfo->unit = sdInfo->H_end * sdInfo->S_end;
}
/
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;
}
}
}
/
int get_sd_block_count(char *devicefile)
{
FILE *fp;
char buf[128];
int block_count = 0;
int nbytes = 0;
char *t = "/sys/block/";
char sd_size_file[64];
strcpy(sd_size_file, t);
strcat(sd_size_file, &devicefile[5]);//例如 以argv[1]为参数,argv[1] = “/dev/sdb”从第五位开始取得SD卡的设备名称
strcat(sd_size_file, "/size");//连接形成字符串路径/sys/block/dev/sdb/size
fp = fopen(sd_size_file, "rb");//打开文件sys/block/dev/sdb/size
nbytes = fread(buf, 1, 128, fp);//读出sdb的容量
fclose(fp);
block_count = atoi(buf);//将容量字符串转换成整数,以扇区位单位
return block_count;//返回sd卡的总扇区
}
/
int main(int argc, char *argv[])
{
FILE *fp;
int total_block_count;
int block_start = 0, block_offset = 0;
SDInfo sdInfo;
PartitionInfo partInfo[4];//创建四个分区的结构体
unsigned char mbr[512];分区的mbr参数
if (argc != 2)
{
printf("Usage: sd_fdisk <device_file>\n");
return -1;
}
///
memset((unsigned char *)&sdInfo, 0x00, sizeof(SDInfo));
///
total_block_count = get_sd_block_count(argv[1]);//获得sd卡的总扇区数目
get_SDInfo(total_block_count, &sdInfo);
/*
///
//
block_start = calc_unit(_10MB, sdInfo);//计算10MB在SD卡中的扇区序号
block_offset = calc_unit(_100MB, sdInfo);//计算100MB在SD卡中的扇区序号
///
partInfo[0].bootable = 0x00;
partInfo[0].partitionId = 0x83;
make_partitionInfo(block_start, block_offset, sdInfo, &partInfo[0]);
///
block_start += block_offset;
partInfo[1].bootable = 0x00;
partInfo[1].partitionId = 0x83;
make_partitionInfo(block_start, block_offset, sdInfo, &partInfo[1]);
///
block_start += block_offset;
partInfo[2].bootable = 0x00;
partInfo[2].partitionId = 0x83;
make_partitionInfo(block_start, block_offset, sdInfo, &partInfo[2]);
*/
///
// block_start += block_offset;
block_start = calc_unit(_10MB, sdInfo);//跳过前10MB空间,应该是覆盖了前面三个分区,这里也没搞懂,前面三个分区应该是废代码
block_offset += BLOCK_END;//偏移量为BLOCK_END个扇区
partInfo[3].bootable = 0x00;
partInfo[3].partitionId = 0x0C;
make_partitionInfo(block_start, BLOCK_END, sdInfo, &partInfo[3]);//创建分区数据结构
///
memset(mbr, 0x00, sizeof(mbr));
mbr[510] = 0x55; mbr[511] = 0xAA;//重新写mbr分区表
// encode_partitionInfo(partInfo[0], &mbr[0x1CE]);
// encode_partitionInfo(partInfo[1], &mbr[0x1DE]);
// encode_partitionInfo(partInfo[2], &mbr[0x1EE]);
encode_partitionInfo(partInfo[3], &mbr[0x1BE]);将分区数据结构写入sd卡的mbr数组
fp = fopen("sd_mbr.dat", "wb");
fwrite(mbr, 1, sizeof(mbr), fp);将分区内容写入文件,以备后面写入SD卡0扇区
fclose(fp);
return 0;
}
注:
1.CHS与LBA寻址
早期系统就是直接使用磁头柱面和扇区来对硬盘进行寻址(这称为CHS寻址),这需要分别存储每个区域的三个参数(这称为3D参数),使用时再分别读取三个参数,然后再在送到磁盘控制器去执行。由于系统用8b来存储磁头地址,用10b来存储柱面地址,用6b来存储扇区地址,而一个扇区共有512B,这样使用CHS寻址一块硬盘最大容量为256 * 1024 * 63 * 512B = 8064 MB(1MB = 1048576B)(若按1MB=1000000B来算就是8.4GB)。
随着硬盘技术的进步,硬盘容量越来越大,CHS模式无法管理超过8064 MB的硬盘,因此工程师们发明了更加简便的LBA寻址方式。在LBA地址中,地址不再表示实际硬盘的实际物理地址(柱面、磁头和扇区)。LBA编址方式将CHS这种三维寻址方式转变为一维的线性寻址,它把硬盘所有的物理扇区的C/H/S编号通过一定的规则转变为一线性的编号,系统效率得到大大提高,避免了烦琐的磁头/柱面/扇区的寻址方式。在访问硬盘时,由硬盘控制器再将这种逻辑地址转换为实际硬盘的物理地址。用LBA值除以2可以大体估算出这个硬盘的容量是多少B。
2.MBR
主引导记录(Master Boot Record,缩写:MBR),又叫做主引导扇区,是计算机开机后访问硬盘时所必须要读取的首个扇区,它在硬盘上的三维地址为(柱面,磁头,扇区)=(0,0,1)。在深入讨论主引导扇区内部结构的时候,有时也将其开头的446字节内容特指为“主引导记录”(MBR),其后是4个16字节的“磁盘分区表”(DPT),以及2字节的结束标志(55AA)。因此,在使用“主引导记录”(MBR)这个术语的时候,需要根据具体情况判断其到底是指整个主引导扇区,还是主引导扇区的前446字节。
对于硬盘而言,一个扇区可能的字节数为128×2n (n=0,1,2,3)。大多情况下,取n=2,即一个扇区(sector)的大小为512字节。
启动代码
主引导记录最开头是第一阶段引导代码。其中的硬盘引导程序的主要作用是检查分区表是否正确并且在系统硬件完成自检以后将控制权交给硬盘上的引导程序(如GNU GRUB)。 它不依赖任何操作系统,而且启动代码也是可以改变的,从而能够实现多系统引导。
硬盘分区表
硬盘分区表占据主引导扇区的64个字节(偏移01BEH--偏移01FDH),可以对四个分区的信息进行描述,其中每个分区的信息占据16个字节。具体每个字节的定义可以参见硬盘分区结构信息。下面是一个例子:
如果某一分区在硬盘分区表的信息如下
- 80 01 01 00 0B FE BF FC 3F 00 00 00 7E 86 BB 00
则我们可以看到,最前面的"80"是一个分区的激活标志,表示系统可引导[1];"01 01 00"表示分区开始的磁头号为1,开始的扇区号为1,开始的柱面号为0;"0B"表示分区的系统类型是FAT32,其他比较常用的有04(FAT16)、07(NTFS);"FE BF FC"表示分区结束的磁头号为254,分区结束的扇区号为63、分区结束的柱面号为764;"3F 00 00 00"表示首扇区的相对扇区号为63;"7E 86 BB 00"表示总扇区数为12289662。
对于现代大于8.4G的硬盘,CHS已经无法表示, BIOS使用LBA模式,对于超出的部分,CHS值通常设为 FEFFFF, 并加以忽略,直接使用08-0f的4字节相对值,再进行内部转换.
结束标志字
结束标志字55,AA(偏移1FEH-偏移1FFH)是主引导扇区的最后两个字节,是检验主引导记录是否有效的标志。
![](https://img-blog.csdn.net/20131220190643328)
上图是一个8G卡经过这个程序后的分区表,其中0x1BE = 0,表示是非激活分区,0x1BF = 0x00,开始磁头H是0,0x1C0=0x01,开始扇区S是1,0x1C1=0,开始柱面C是0……0x1C2=0x0C表示是FAT32分区,0x1C3 = 0xFC,表示结束磁头H是252,0X1C4 = 0xFC,取后六位是0x3C,表示结束扇区S是60,0x1C5=0xFB,和前面那个字节的前两位组成0x3FB,表示结束柱面C是1019……