eMMC燒錄鏡像分區信息剖析

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
 正常

作者:帥得不敢出門

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章