MTD分区

        有三种分区的方法:

  1. 控制器驱动提供分区表
  2. bootloader命令行
  3. 变相使用bootloader命令行

控制器驱动分区

  在控制器驱动中写死,初始化flash时设置。参考 ​​mtd子系统 代码流程​

在内核的flash控制器驱动提供分区信息,并注册(mtd_device_register()函数)

分区数据结构

数据结构对应mtd_device_register()函数的第三个参数,是分区的结构体数组指针。

例如:

#define MTD_PARTITION(na, off, sz, flgs) \

    { \

        .name = na, .offset = off, \

        .size = sz, .mask_flags = flgs, \

    }


struct mtd_partition parts[MAX_MTD_PARTITIONS] = {

        MTD_PARTITION("boot",    MTDPART_OFS_APPEND,    SZ_1M,        MTD_WRITEABLE),

        MTD_PARTITION("env",     MTDPART_OFS_APPEND,    SZ_256K,      MTD_WRITEABLE),

        MTD_PARTITION("kernel0",  MTDPART_OFS_APPEND,   SZ_8M,        0),

};

bootloader命令行分区

  u-boot将分区信息(形如:mtdparts=xxx)添加到bootargs中,kernel在启动的时候会解析mtdparts。

示例: " ... mtdparts=samsung-nand0:256k(boot)ro,8m(kernel)ro,16m(ubi-img),-(user)"

注意:中间不能有空格。

内核配置

  内核中的mtd驱动必须要支持,即内核配置时需要选上

       Device Drivers  --->

            Memory Technology Device (MTD) support  --->

                Command line partition table parsing

即:CONFIG_MTD_CMDLINE_PARTS=y

mtdparts的格式

    mtdparts=<mtddef>[;<mtddef]

    <mtddef>  := <mtd-id>:<partdef>[,<partdef>]

    <partdef> := <size>[@offset][<name>][ro]

    <mtd-id>  := unique id used in mapping driver/device

    <size>    := standard linux memsize OR "-" to denote all remaining space

    <name>    := (NAME)

因此你在使用的时候需要按照下面的格式来设置:

     mtdparts=mtd-id:<size1>@<offset1>(<name1>),<size2>@<offset2>(<name2>)

注意事项

1. mtd-id 必须要跟当前控制器驱动指定的mtd->name一致。

2. 在bootargs参数列表中,  可以指定当前flash的mtd-id,指定 mtdids:nand0=gen_nand.1,前面的nand0则表示第一个flash

3. size在设置的时候有三种方法:

     a. 可以为实际的size:" ... mtdparts=samsung-nand0:0x4000(boot)ro,0x800000(kernel)ro,0x1000000(ubi-img),-(config)"

     b. 用简写的单位:" ... mtdparts=samsung-nand0:256k(boot)ro,8m(kernel)ro,16m(ubi-img),-(config)"

           支持的单位:k,K,m,M,g,G,t,T,p,P,e,E。

     c. 也可以为'-'这表示剩余的所有空间。

4. mtdparts=tq2440-0:1m@0(spl)ro,1m(u-boot)ro,3m(kernel)ro,-(rootfs)          等价于

     mtdparts=tq2440-0:1m(spl)ro,1m(u-boot)ro,3m(kernel)ro,-(rootfs)

 详见:​drivers/mtd/cmdlinepart.c 注释

mtdparts命令

  为了实现mtdparts分区命令的支持需要在U-boot-2010.06/include/configs/at91sam9263ek.h中添加相关的宏定义

  #define CONFIG_CMD_MTDPARTS

  #define CONFIG_MTD_DEVICE

  #define CONFIG_MTD_PARTITIONS


  加入MTD分区信息:

  #define MTDIDS_DEFAULT "nand0=atmel_nand"

  #define MTDPARTS_DEFAULT          "mtdparts=atmel_nand:15M@0(cramfs)," \

                                    "15M(jffs2)," \

                                    "30M(yaffs2)," \

                                    "-(user)"

保存后退出,回到根目录,重新make

命令用法:

mtdparts  查看分区信息

mtdparts  恢复默认分区

setenv mtdparts xxx   设置分区信息

源码分析(mtdparts)

获得命令行内容

解析bootargs环境变量时,若有“mtdparts”,则将cmdline指向后边的字符串,后边控制器probe时会调用到它。

drivers/mtd/cmdlinepart.c

static int __init mtdpart_setup(char *s)

{

    cmdline = s;

    return 1;

}

__setup("mtdparts=", mtdpart_setup);

static struct mtd_part_parser cmdline_parser = {

    .parse_fn = parse_cmdline_partitions,

    .name = "cmdlinepart",

};

cmdline_parser_init(void)

      register_mtd_parser(&cmdline_parser)

根据命令行进行分区  ​   

xxx_fmc_probe                                //xxx_fmc.c             drivers/mtd/nand/

      mtd_device_register(mtd, parts, nr_parts)                                 //mtd.h  include/linux/mtd

            ret = mtd_device_parse_register(master, NULL, NULL, parts, nr_parts)           //mtdcore.c  drivers/mtd

                  parse_mtd_partitions(mtd, types, &parsed, parser_data)                           //mtdpart.c  drivers/mtd

                        (*parser->parse_fn)(master, &pparts->parts, data)

                        //drvers/mtd/cmdlinepart.c=>cmdline_parser_init=> register_mtd_parser(&cmdline_parser)

                        parse_cmdline_partitions                  //cmdlinepart.c drivers/mtd

                              const char *mtd_id = master->name;      


                              mtdpart_setup_real(cmdline)       //cmdlinepart.c drivers/mtd

                                    对于每一个命令行的分区      (对于每一个mtd-id)

                                          调用newpart()解析分区

                                          保存命令行mtdparts=xxx:yyy的xxx

                                          将解析出来的添加到partitions链表       


                              如果mtd_id与所有的xxx都不相同,则直接退出。


            如果解析命令行成功,且分区数目大于0,则使用命令行分区

            如果命令行没提供或者解析错误,则使用驱动提供的分区

            后续分析见 ​​mtd子系统 控制器驱动流程​

变相使用命令行

  例如:在内核里边,修改代码,将配置写死。

EMMC分区

一般使用命令行,或者变相使用命令行。

使用bootloader命令行

示例

    设置bootargs环境变量添加  "blkdevparts=mmcblk0:1m(boot),8m(kernel),256m(ext4-img),-(user)"

格式

总格式:blkdevparts=<blkdev-def>[;<blkdev-def>]

<blkdev-def>  格式: <blkdev-id>:<partdef>[,<partdef>]

 <partdef> 格式:       <size>[@<offset>](part-name)

<blkdev-id>

    block device disk name, embedded device used fixed block device,

    it's disk name also fixed. such as: ​mmcblk0, mmcblk1, mmcblk0boot0​.

<size>

    partition size, in bytes, such as: ​512, 1m, 1G​.

<offset>

    partition start address, in bytes.

(part-name)

    partition name, kernel send uevent with "PARTNAME". application can create

    a link to block device partition with the name "PARTNAME".

    user space application can access partition by partition name.

Example:

    eMMC disk name is "mmcblk0" and "mmcblk0boot0"

  bootargs:

    'blkdevparts=mmcblk0:1G(data0),1G(data1),-;mmcblk0boot0:1m(boot),-(kernel)'

  dmesg:

    mmcblk0: p1(data0) p2(data1) p3()

    mmcblk0boot0: p1(boot) p2(kernel)

详见:​kernel/Documentation/block/cmdline-partition.txt

注意

对于“blkdevparts=”后边紧跟的这个名字,格式是固定的,必须是mmcblk​m​或者mmcblk​m​boo​tn

这个是控制器的序号,详见 ​​MMC子系统分析 控制器源代码分析​

源码分析(blkdevparts)

获得命令行内容

解析bootargs环境变量时,若有“blkdevparts”,则将cmdline指向后边的字符串,后边控制器probe时会调用到它。

kernel/block/partitions/cmdline.c

static int __init cmdline_parts_setup(char *s)

{

    cmdline = s;

    return 1;

}

__setup("blkdevparts=", cmdline_parts_setup);

根据命令行进行分区

调用到此处的流程见 ​​MMC子系统分析 控制器源代码分析​

mmc_rescan    //core.c drivers/mmc/core/

    mmc_attach_mmc

        mmc_add_card

            device_add

                bus_probe_device

                    __device_attach

                        bus_for_each_drv

                            driver_probe_device

                                mmc_blk_probe

                                    mmc_add_disk

                                        device_add_disk

                                            blkdev_get

                                                __blkdev_get

                                                    rescan_partitions

                                                        check_partition

                                                            调用check_part[]数组成员

                                                                cmdline_partition

kernel/block/partitions/cmdline.c

static char *cmdline;

struct cmdline_parts *bdev_parts;

cmdline_partition()

    //传进来的参数名为state

    char bdev[BDEVNAME_SIZE];

    //cmdline现在已经指向 "mmcblk0:1m(boot),8m(kernel),256m(ext4-img),-(user)"    //​解析每一个由;分开的分区

    cmdline_parts_parse(&bdev_parts, cmdline)     //cmdline-parser.c    kernel/block/

            //​解析每一个由:开始的分区

            parse_parts(next_parts, pbuf)     //cmdline-parser.c    kernel/block/

                    //​解析每一个由,分开的分区

                    parse_subpart(next_subpart, buf);     //cmdline-parser.c    kernel/block/


    bdevname(state->bdev, bdev);            //block/partition-generic.c

            从state->bdev->bd_disk获得disk名字(一般是mmcblk)\

            从state->bdev->bd_part->partno获得设备号

            将其组成字符串,赋值给bdev

    cmdline_parts_find(bdev_parts, bdev);   //block/cmdline-parser.c

            将bdev(控制器初始化时生成的)与所有bdev_parts->name(解析命令行生成的,是 "blkdevparts="到 ":"

         之间的字符串)比较,

    如果没有匹配的,则返回

    //获得容量

    disk_size = get_capacity(state->bdev->bd_disk) << 9;

    //根据命令行设置分区数据(偏移、大小等)

    cmdline_parts_set(parts, disk_size, 1, add_part, (void *)state);

若成功,则之后会​打印​:mmcblk0: p1(boot) p2(kernel) p3(ext4-img) p4(user)      

变相使用命令行

       kernel/block/partitions/cmdline.c => cmdline_partition 直接在开始给cmdline赋值,例如:

       cmdline = "mmcblk0:1m(boot),512k(env),8m(kernel),8m(config),-(user)"