eMMC和一般硬盘类似,分区信息位于 mmcblk0 的 0 扇区,内核不负责分区的创建,仅仅是读0扇区MBR及分区表即来获得分区信息。
这里讨论的是非GPT的分区问题
eMMC的镜像生成需要用到genimage工具,其需要改配置文件
具体可查阅官方 https://github.com/pengutronix/genimage
比如
genimage --rootpath "genimage/root" --tmppath "genimage/tmp" --inputpath "genimage/input" --outputpath "genimage/image" --config "genimage/gen.cfg"
它会根据gen.cfg中设置的分区信息来自动生成各分区镜像及总镜像
通过genimage生成了eMMC的镜像,再烧录到eMMC后,启动板子后却发现分区不对,分了8个区(包含一个扩展分区信息)加载后发现最后一个15G的分区识别不出来,
一开始感觉是是哪里出了BUG,导致不支持扩展分区上的逻辑分区大于3个,后面发现这个方向不对,因为用fdisk去看烧录的镜像,可以正确看到所有的分区都是在的
/sbin/fdisk -l ./sdcard.img
Disk ./sdcard.img:286 MiB,299895296 字节,585733 个扇区
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0x00000000
设备 启动 起点 末尾 扇区 大小 Id 类型
./sdcard.img1 * 1 12288 12288 6M c W95 FAT32 (LBA)
./sdcard.img2 12289 217088 204800 100M 0 空
./sdcard.img3 * 217089 237568 20480 10M c W95 FAT32 (LBA)
./sdcard.img4 237569 33554431 33316863 15.9G f W95 扩展 (LBA)
./sdcard.img5 237570 368641 131072 64M 83 Linux
./sdcard.img6 368643 380930 12288 6M c W95 FAT32 (LBA)
./sdcard.img7 380932 585731 204800 100M 0 空
./sdcard.img8 585733 33554431 32968699 15.7G 83 Linux
总共16G的eMMC
可以看到第一行起点是1,代表扇区,0扇区就是MBR (一个扇区512字节)
第四行起点是237569,第五行起点是237570,代表第四行的分区占用了1个扇区,这一行是扩展分区,它接在三个主分区后面, 大小是15.9G
5~8这4个分区都是分在扩展分区里的逻辑分区, 且逻辑分区间会有1个扇区的间隔,不同于主分区无间隔,这1个扇区是逻辑分区的分区表信息
MBR
dd if=./sdcard.img of=mmc_mbr.bin bs=512 count=1
MBR可以看到三个主分区及扩展分区,逻辑分区则看不到的
/sbin/fdisk -l ./mmc_mbr.bin
读取扩展分区表失败(偏移=237569): 没有那个文件或目录
Disk ./mmc_mbr.bin:512 B,512 字节,1 个扇区
设备 启动 起点 末尾 扇区 大小 Id 类型
./mmc_mbr.bin1 * 1 12288 12288 6M c W95 FAT32 (LBA)
./mmc_mbr.bin2 12289 217088 204800 100M 0 空
./mmc_mbr.bin3 * 217089 237568 20480 10M c W95 FAT32 (LBA)
./mmc_mbr.bin4 237569 33554431 33316863 15.9G f W95 扩展 (LBA)
扩展分区
每次能看到2个分区的信息
逻辑分区1,2
dd if=./sdcard.img of=partition.bin bs=512 count=1 skip=237569
/sbin/fdisk -l partition.bin
读取扩展分区表失败(偏移=131073): 没有那个文件或目录
设备 启动 起点 末尾 扇区 大小 Id 类型
partition.bin1 1 131072 131072 64M 83 Linux
partition.bin2 131073 143361 12289 6M f W95 扩展 (LBA)
逻辑分区2,3
dd if=./sdcard.img of=partition2.bin bs=512 count=1 skip=368642
/sbin/fdisk -l partition2.bin
读取扩展分区表失败(偏移=143362): 没有那个文件或目录
设备 启动 起点 末尾 扇区 大小 Id 类型
partition2.bin1 1 12288 12288 6M c W95 FAT32 (LBA)
partition2.bin2 143362 348162 204801 100M f W95 扩展 (LBA)
逻辑分区3, 4
dd if=./sdcard.img of=partition3.bin bs=512 count=1 skip=380931
/sbin/fdisk -l partition3.bin
读取扩展分区表失败(偏移=348163): 没有那个文件或目录
Disk partition3.bin:512 B,512 字节,1 个扇区
设备 启动 起点 末尾 扇区 大小 Id 类型
partition3.bin1 1 204800 204800 100M 0 空
partition3.bin2 348163 33316862 32968700 15.7G f W95 扩展 (LBA)
烧录后,在板子上查看分区表信息,如下最后一个分区活生生被吃了,只有1到7,少了8
/ # cat /proc/partitions
major minor #blocks name
1 0 8192 ram0
1 1 8192 ram1
31 0 512 mtdblock0
31 1 256 mtdblock1
31 2 64 mtdblock2
31 3 512 mtdblock3
31 4 640 mtdblock4
31 5 64 mtdblock5
179 0 15267840 mmcblk0
179 1 6144 mmcblk0p1
179 2 102400 mmcblk0p2
179 3 10240 mmcblk0p3
179 4 1 mmcblk0p4
179 5 65536 mmcblk0p5
179 6 6144 mmcblk0p6
179 7 102400 mmcblk0p7
179 24 4096 mmcblk0boot1
179 12 4096 mmcblk0boot0
/proc/partitions这个是由内核生成的,可以定位到是内核出了问题,只能从源码上着手分析了
eMMC kernel加载过程
mmc_blk_probe->block.c
mmc_add_disk->
add_disk->genhd.c
register_disk->
blkdev_get->block_dev.c
__blkdev_get->
rescan_partitions->partition-generic.c
check_partition
add_partition // 这里添加分区信息
//add_partition是由rescan_partition调用的
//block/partition-generic.c
int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
{
/* add partitions */
for (p = 1; p < state->limit; p++) { // 注意这里state->limit限制了分区数
part = add_partition(disk, p, from, size,
state->parts[p].flags,
&state->parts[p].info);
}
}
// state->limit分区数限制是从哪里来的呢,是从下面这个disk_max_parts来的
static inline int disk_max_parts(struct gendisk *disk)
{
if (disk->flags & GENHD_FL_EXT_DEVT)
return DISK_MAX_PARTS;
return disk->minors; // 到这里就变成由minors限定了
}
// 这里会把disk_max_parts返回的值赋值给state->limit
//block/partitions/check.c
static struct parsed_partitions *allocate_partitions(struct gendisk *hd)
{
struct parsed_partitions *state;
int nr;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return NULL;
nr = disk_max_parts(hd); // 获取最大分区数
state->parts = vzalloc(nr * sizeof(state->parts[0]));
if (!state->parts) {
kfree(state);
return NULL;
}
state->limit = nr; // 这里对数量做了限制
return state;
}
// limit由minors来的,那么minors又是从哪里赋值来的呢,看下面这个函数
//drivers/mmc/card/block.c
// 这里predev_minor从CONFIG_MMC_BLOCK_MINORS赋值来的,看到有CONFIG_XX就可以猜测是内核的配置中设置的
// 所以如果eMMC镜像写入后,kernel再加载出来分区数少了,很可能是这个CONFIG_MMC_BLOCK_MINORS的值设置小了
// 原因大致是定位到了,接下来是为了验证这个猜想, 看代码
static int perdev_minors = CONFIG_MMC_BLOCK_MINORS;
static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
struct device *parent,
sector_t size,
bool default_ro,
const char *subname,
int area_type)
{
md->disk = alloc_disk(perdev_minors); // 在这里由perdev_minors赋值来的, alloc_disk的函数名就叫minors
}
// 这里看函数原型
//block/genhd.c
struct gendisk *alloc_disk(int minors)
{
return alloc_disk_node(minors, NUMA_NO_NODE);
}
EXPORT_SYMBOL(alloc_disk);
struct gendisk *alloc_disk_node(int minors, int node_id)
{
disk->minors = minors; // 这里就是minors的真正赋值的地方
}
修改kernel config中的 CONFIG_MMC_BLOCK_MINORS 为9后就正常了(只要大于8就行), 原始的配置就是8, 所以就少了一个分区
/ # cat /proc/partitions
major minor #blocks name
1 0 8192 ram0
1 1 8192 ram1
31 0 512 mtdblock0
31 1 256 mtdblock1
31 2 64 mtdblock2
31 3 512 mtdblock3
31 4 640 mtdblock4
31 5 64 mtdblock5
179 0 15267840 mmcblk0
179 1 6144 mmcblk0p1
179 2 102400 mmcblk0p2
179 3 10240 mmcblk0p3
179 4 1 mmcblk0p4
179 5 65536 mmcblk0p5
179 6 6144 mmcblk0p6
179 7 102400 mmcblk0p7
179 8 14974973 mmcblk0p8
179 24 4096 mmcblk0boot1
179 12 4096 mmcblk0boot0
正常
作者:帅得不敢出门