SSD_系统分区


1. 分区介绍


1.1. Flash基本分区介绍

分区表保存在编译输出的partition_layout.txt中,如下图所示,下图的分区配置在不同的软件版本上可能会有差异,请以当前编译的代码为准:

SPI-NOR分区表信息:

SPI-NAND分区表信息:

CIS:

Spinand保存在flash 0地址的位置,Nor保存在IPL分区之后,它包含三个部分内容:

  • GCIS

    这部分包含一种通用的flash info信息,目的是让rom code适配绝大多数flash的参数,使boot能够顺利进到IPL。

  • flash info

    保存flash的一些基本信息,例如block page count、block count等,简称SNI,这些信息会根据flash的种类而改变。

  • partition info

    保存的分区信息,简称PNI,由于只有IPL/IPL_CUST/RTK才能看到PNI中的信息,而当linux系统起来后,分区信息就被替换成uboot的mtdparts,走linux原生的分区流程。

    在mtdparts中除了用于制作One bin的部分的BOOT分区与PNI中有不同之外,其他的分区信息都必须与PNI中相等。不同的是从PNI中可以看到在mtdpars的BOOT分区里面更加细化的分布,例如其中IPL/IPL_CUST/UBOOT具体所在的位置。

IPL/IPL_CUST:

CPU上电后首先跑到的是rom code,顾名思义代码保存在特殊的ROM中,且是只读的。ROM code跑完后会找到IPL的位置,解析IPL的地址的位置的做法,NOR和SPINAND还有差别下面章节会做相关的介绍。IPL里主要功能是做一些基础的硬件初始化,例如设定当前DDR参数,以及GPIO/IIC相关等。

IPL初始化的是一些共有的硬件模块,IPL_CUST中会根据当前板子的实际情况初始化客制化板子硬件的可执行的二进制文件,例如客制化的GPIO管脚,IIC配置。

UBOOT: UBOOT的二进制文件存放分区。

ENV: UBOOT的环境变量存放分区。

**LOGO:**存放的是开机logo相关的配置。

**MISC:**存放一些系统配置脚本的分区,一般为littlefs的文件系统,其中包含bootlogo、屏参等相关init配置文件和一些资源文件,若有配置此分区就没有必要再添加LOGO分区,详情请参考BOOT LOGO使用参考

**KERNEL:**存放内核的二进制文件。

**ROOTFS:**根目录文件系统。

UBI: UBI的内容在上图分区表中不会显示出来,UBI中会创建多个ubifs格式的子分区,客户可以根据需要自行创建。Spinand的miservice分区就是放在UBI中。

**APPLICATION/CUSTOMER:**用户扩展分区。

**MISERVICE:**存放mi各个模块的ko,so,还有一些配置脚本和bin,下一节有具体介绍。


1.2. MISERVICE分区内容

在MISERVICE分区的内容被mount到了根目录/config/下面,下表中加深字体的文件是必须保持路径一致的,否则有可能会导致系统无法启动。

└── config
    ├── board.ini                           ---->保存当前板子的信息
    ├── config_tool                         ---->mi sys初始化mmap内存的应用
    ├── dump_config -> config_tool          ---->dump config 应用,config_tool的symbol link。
    ├── dump_mmap -> config_tool            ---->dump mmap应用,config_tool的symbol link。
    ├── fbdev.ini                           ---->Framebuffer 配置,若无fbdev需求可删除。
    ├── iqfile
    │   ├── imx307_iqfile.bin
    │   ├── iqfile0.bin -> imx307_iqfile.bin    ---->Sensor IQ相关的bin
    │   ├── iqfile1.bin -> imx307_iqfile.bin    
    │   ├── iqfile2.bin -> imx307_iqfile.bin    
    │   ├── iqfile3.bin -> imx307_iqfile.bin    
    │   └── isp_api.xml
├── mmap.ini                                ---->Mmap。Mmap可以参考Memory Layout介绍
    ├── model                               ---->模块相关配置,可以删除。
    │   ├── Customer_1.ini
    │   ├── Customer_2.ini
    │   └── Customer_auto_test.ini
    ├── modules                             ---->系统KO存放路径。
    │   └── 4.9.84
│       ├── cifs.ko
        ……
    ├── pq                                  ---->NVR显示PQ相关。
    │   ├── Bandwidth_RegTable.bin
    │   ├── Main.bin
    │   └── Main_Ex.bin
    ├── terminfo                            ---->Console显示相关。
    │   └── v
│       ├── vt100
        ……
    └── venc_fw                             ---->Encoder的firmware
        └── chagall.bin

2. CIS分区结构

从上文介绍可以了解,CIS的分区中分为三部分,从flash的0地址开始算起,分别为GCIS、PNI、SNI。


2.1. PNI

通过工具pnigenerator动态生成partinfo.pni,在编译后会在image/boot/中以单独的文件形式保存。

代码和编译:

pnigenerator工具的源码路径:

project/image/makefiletools/src/pnigenerator/pnigenerator.c

利用服务器上的gcc编译:

gcc pnigenerator.c -Wall –o pnigenerator

使用举例:

pnigenerator -c 1024 -s 0x20000 -a "0x140000(CIS),0x60000(IPL),0x60000(IPL_CUST),0xC0000(UBOOT)" -b "0x60000(IPL),0x60000(IPL_CUST),0xC0000(UBOOT)" -t "0x60000(ENV),0x20000(KEY_CUST),0x500000(KERNEL),0x500000(RECOVERY),0x600000(rootfs),0x60000(MISC),-(UBI)" -o /home/malloc.peng/hc/project/image/output/images/boot/partinfo.pni

参数定义:

-r: 读取目标PNI文件中的分区并打印。

-c: 所有分区所占FLASH的BLOCK的总和。

-s: FLASH一个BLOCK的data size,单位为byte。

-a: BOOT PART分区的集合,表示成mtdparts的语法格式

-t: SYS PART分区的集合,表示成mtdparts的语法格式

-o: 生成PNI文件的文件名和路径

PNI文件并不是静态地存放在打包project中,它是根据用户使用的编译配置而定,由pnigenerator工具根据传入的配置分区的参数生成一个bin文件。

PNI通过“-a”、“-t”参数传入分区的信息生成文件project/image/output/images/boot/partinfo.pni


2.2. SNI

SNI在ALKAID的board存放的路径:

  • SPINAND:

    project/board/$(CHIP)/boot/spinand/partition/flash_list.sni
    
  • NOR:

    project/board/$(CHIP)/boot/nor/partition/flash_list.nri
    

2.3. GCIS

GCIS是一种特殊的SNI,它包含的是通用的FLASH初始化参数,用于给ROM CODE中固化的代码读取用于启动FLASH的参数,最终目的是读取IPL的data,使BOOT阶段启动到IPL中。

SNI在ALKAID的board存放的路径:

  • SPINAND:

    project/board/$(CHIP)/boot/spinand/partition/flash.sni
    
  • NOR:

    project/board/$(CHIP)/boot/nor/partition/flash.nri
    

2.4. CIS分区数据结构

CIS的分区由上述的GCIS、PNI、SNI按照特定的方式拼接而成,通过Makefile脚本动态生成。

上图表示的是CIS数据中的结构,GCIS和PNI由于都小于一个PAGE的大小,因此他们各占一个PAGE的空间,SNI_LIST中由于保存了所有的FLASH信息,它会追加到PAGE1的后面。请注意NOR FLASH和SPINAND FLASH的PAGE大小不一定是2k或者4K,它跟FLASH的规格有关,具体请参考FLASH的SPEC。

PAGE的大小可以根据分区的config脚本中定义的cis$(PGSIZE)来定义。

在分区的烧录脚本制作的过程中,CIS分区的bin制作和烧录也是其中一个环节,整个制作过程后面会介绍,CIS的bin的制作在project/image/image.mk中的目标cis_nofsimage中执行。

制作完成后会在project/image/output/images/中生成的cis.bin就包含GCIS/PNI/SNI这三个内容。


3. 分区配置介绍


3.1. 分区配置前注意事项

无论是spi-nor flash上的分区,还是spi-nand flash上的分区。都可归为两类:

  • 不建议改动的boot

  • 可以变动的sys

这一章节介绍的分区变更都是在sys这部分上,包括如何配置脚本变更分区顺序,如何打包分区,如何生成烧录脚本,若用户有自己的一套打包和烧录的流程,可以不用关心这一章,需要特别注意的是在上一章中miservice分区中有特别标注出来的文件需要放到指定的路径下系统才能跑起来,所以在制作分区时务必在根目录下创建/config/,并把相关的文件放到此路径下。


3.2. 脚本配置介绍

分区变更分为分区制作、打包和分区烧录脚本制作这两部分。

下面流程图的两个分支,左边走的是分区制作、打包流程,右边走的是分区烧录脚本制作的流程,分区烧录脚本仅支持tftp升级脚本,暂不支持usb升级脚本制作。


3.3. 分区配置脚本

分区配置的脚本在:

project/image/config/$(CHIP)/xxx.partition.config

这是总的分区配置信息脚本,所有与分区调整相关的代码都在这个config文件中实现,这个config文件会有很多份,每个不同的chip、spinand分区还是spinor分区都会有各自的分区config文件。

通过build config,就可以找到该项目使用的分区config文件是什么,定义的变量是IMAGE_CONFIG。

此配置脚本中,变量IMAGE_CONFIG可以找到对应使用的分区配置脚本。

在config脚本文件中,每个分区由一组变量表示,如下图所示:

请参考上一章CIS分区结构,CIS分区很特殊,存放的是一组规定的分区配置raw data,这个是一个特殊的分区,特殊的分区由特别的指令生成,下文会介绍。

分区分为带文件系统和不带文件系统的,他们的打包和烧录脚本制作完全不一样,带文件系统的分区镜像文件制作可以统一通过工具处理,而不带文件系统的分区,则需要针对每个分区单独处理。

上文介绍了一些公版所使用的分区,例如miservice分区,它可以配置成只读的squashfs文件系统,也可以做成jiffs2或者ubifs,所有公版所使用的分区都由xxx.mk脚本用于填充镜像文件内部的文件内容,很显然地,miserive.mk中就会把mi所需要的所有ko和so拷贝的目标文件夹中,rootfs.mk就会把根文件系统内部所需要的文件拷贝到目标文件夹,例如rootfs.mk中就要拷贝linux所需要的busybox工具。

miserive分区配置举例:


3.4. 分区位置变更

在CIS分区的配置中,变量cis(BOOTTAB0)、cis(BOOTTAB1)、cis$(SYSTAB)用于配置分区信息。

cis(BOOTTAB0)和cis(BOOTTAB1)用于在one bin制作中指定其A-B分区的做法,这种做法会在PNI中标识其中使用的BOOT是否是活动的,目前在spi-nand分区中有引入A-B分区,其中BOOTTAB0是活动的分区,当BOOT0在烧录出现异常或者出现坏快的情况下会从BOOT0直接跳到BOOT1的备份分区中,并在PNI中重新标识活动的分区,下次启动时会自动跑到活动的分区中。

BOOTTAB0、BOOTTAB1和SYSTAB为分区表的定义,它使用的语法格式是mtdparts所支持的,格式可以写成:

partition0_size(partition0_name), partition0_size(partition0_name),……

例如下面CIS和IPL分区的描述格式:

0x14000(CIS),0x60000(IPL)

每个分区都由逗号隔开。若没有指定分区的起始位置,则从第一个分区开始算,分区默认是从FLASH的0地址开始。若需要从分区的指定位置配置,则按照以下语法格式:

partition0_size@offset(partition0_name)

指定了分区的起始位置后,在这个分区后面追加的分区如果没有指定其位置,它的起始位置则会在当前分区之后的分区位置逐个增加。 若当前分区在分区表中是最后一个,则可以用一个比较方便的方法自适应分区的大小。

例如spinand的UBI分区,在分区表最后,它可以表示成:

xxxx(xxxx), xxxx(xxxx),-(UBI)

分区表解析的逻辑会参考当前flash配置的大小自适应UBI的大小。

分区位置的修改就是改变cis(BOOTTAB0)、cis(BOOTTAB1)、cis$(SYSTAB)这三个变量里面所放分区的位置。一般地BOOT部分的分区是不会改变其位置的,SYSTAB改变的比较常见。


3.5. 增加修改删除jiffs2分区

在spinor的flash上一般用jiffs2作为可读写的分区的文件系统,如下定义了customer分区的相关配置:

customer$(RESOUCE)   = $(OUTPUTDIR)/customer
customer$(FSTYPE)    = jffs2
customer$(PATSIZE)   = 0x5C0000
customer$(MOUNTTG)  = /customer
customer$(MOUNTPT)  = mtd:customer
customer$(OPTIONS)   = ro
customer$(MTDPART) = $(customer$(PATSIZE))(customer)
customer$(OTABLK) = /dev/mtdblock6
  • customer$(RESOUCE):

    需要打包的源文件所在文件夹,这个文件夹中的内容由customer.mk脚本填充。

  • customer$(FSTYPE):

    打包的文件系统类型。

  • customer$(PATSIZE):

    分区大小。

  • customer(MOUNTTG)、customer(MOUNTPT)、customer$(OPTIONS):

    分区在板子起来后需要mount的路径以及参数。rootfs.mk中会处理这些参数。

  • customer$(OTABLK):

    用于OTA升级打包所需的升级设备节点的路径。

3.5.1. 修改

若要修改分区,一般是修改文件系统和分区大小。

当修改文件大小后,要认真核对所有分区的大小累加起来有没有超过flash的size。

可以在partition_layout.txt中查看分区size是否有溢出。

3.5.2. 增加分区

aaa$(RESOUCE)   = $(OUTPUTDIR)/aaa
aaa$(FSTYPE)    = jffs2
aaa$(PATSIZE)   = 0x6B0000
aaa$(MOUNTTG)   = /aaa
aaa$(MOUNTPT)   = mtd:aaa
aaa$(OPTIONS)   = rw
aaa$(OTABLK)   = /dev/mtdblockX
  1. 假设增加一个分区名为aaa,然后配置其大小等信息:

  2. 分区相关的变量配置完成之后,在cis$(SYSTAB) = ”xxx”中指定其放置的位置。

  3. 在变量“IMAGE_LIST”后追加“aaa”。

  4. 在表示需要mount的节点的变量“USR_MOUNT_BLOCKS”后追加“aaa”。

  5. 在分区打包前需要在脚本中添加往$(OUTPUT)/aaa拷贝文件的逻辑。

3.5.3. 删除分区

按照增加分区的操作,反过来执行。

3.5.4. 烧录脚本

系统在script.mk中会根据分区的配置自动生成。


3.6. 增加修改删除squashfs分区

Squashfs的分区变更与jiffs2分区变更大同小异,主要在xxx$(FSTYPE)上有不同,除去rootfs这个比较特别的分区之外,miservice分区制作成squashfs:

miservice$(RESOUCE)   = $(OUTPUTDIR)/tvconfig/config

miservice$(FSTYPE)    = squashfs

miservice$(PATSIZE)   = 0x180000

miservice$(MOUNTTG)  = /config

miservice$(MOUNTPT)  = /dev/mtdblock3

miservice$(OPTIONS)   = ro

与jiffs2分区一样,系统在script.mk中会根据分区的配置自动生成烧录脚本。


3.7. 增加修改删除ubifs分区

UBIFS分区一般应用在spinand上作为可读写的分区文件系统。

UBIFS的所有的分区都属于UBI的mtd block中的一个子分区。一个普通的UBI分区变量设置如下:

miservice$(RESOUCE)   = $(OUTPUTDIR)/miservice/config

miservice$(FSTYPE)    = ubifs

miservice$(PATSIZE)   = 0xA00000

miservice$(MOUNTTG)  = /config

miservice$(MOUNTPT)  = ubi0:miservice

miservice$(OPTIONS)   = rw

与jiffs2分区变更方法不同的是,ubifs分区不需要添加mtdpart讯息,也就是无需在cis$(SYSTAB) = xxx中添加分区位置。


3.8. 增加修改删除LFS分区

Littlefs分区的好处是可以在uboot/linux/rtk中访问。

MISC分区用与存放系统配置脚本相关的文件,例如屏参信息:

misc$(RESOUCE) = $(OUTPUTDIR)/misc
misc$(FSTYPE) = lfs
misc$(PATSIZE) = 0x60000
misc$(MOUNTTG) = /misc
misc$(MOUNTPT) = /dev/mtd4
misc$(OPTIONS) = rw
misc$(MTDPART) = $(misc$(PATSIZE))(MISC)
misc$(OTABLK) = /dev/mtdblock4

修改方式请参考squashfs或者jffs2。


3.9. 特殊分区处理

KERNEL、uboot、IPL、logo、cis等分区属于特殊分区,这些分区没有特别的文件系统,所以无法在Makefile脚本中做集中处理,必须做特别去处理。因此添加一个特殊的分区需要清楚了解如下两个步骤(这里举例的是kernel分区):

  1. image.mk中需要写上分区打包的脚本命令:

    kernel_nofsimage:

    @echo [[$@]]
    
    cp -rvf $($(patsubst %_nofsimage,%,$@)$(RESOUCE)) (IMAGEDIR)/$(patsubst %_nofsimage,%,$@)
    
  2. 在script.mk中写上分区烧录脚本生成的命令:

    kernel_$(FLASH_TYPE)__script:

    @echo "# <- this is for comment / total file size must be less than 4KB" > $(SCRIPTDIR)/[[kernel.es
    @echo tftp $(TFTPDOWNLOADADDR) kernel >> $(SCRIPTDIR)/[[kernel.es
    @echo $(FLASH_PROBE) >> $(SCRIPTDIR)/[[kernel.es
    @echo $(FLASH_ERASE_PART) KERNEL >> $(SCRIPTDIR)/[[kernel.es
    @echo $(FLASH_WRITE_PART) $(TFTPDOWNLOADADDR) KERNEL \$${filesize} >> $(SCRIPTDIR)/[[kernel.es
    @echo "% <- this is end of file symbol" >> $(SCRIPTDIR)/[[kernel.es
    @echo kernel-image done!!!
    

4. ONE BIN


4.1. ONE BIN分区

ONE BIN功能是基于ALKAID的打包脚本通过dd命令使BOOT PART分区合并成一个bin,以uboot和linux kernel的角度来看,可以在uboot中输入命令mtdpart看到当前的分区规划,与pni不同的是BOOT PART这部分,整个BOOT PART不再细化内部分区,而是由一个BOOT分区构成:如下图所示:

  • NOR FLASH:

  • SPINAND FLASH:

ONE BIN打包完成后会在output/images/下面生成一个boot.bin,boot.binipl/ipl_cust/uboot的image拼接而成,nor flash会额外再加上cis分区。


4.2. SPINAND的BOOT分区

在nand flash上,BOOT0/BOOT1属于A/B分区部分,ROM code默认是从BOOT0分区中开启,如果BOOT0分区发生ECC ERROR或者遇到坏快,则会跳到BOOT1分区,并在下次启动后,默认从BOOT1分区中开启,这样可以保证在升级的过程中,BOOT的部分建立了一套备份机制,避免升级BOOT失败后无法进入UBOOT cmd line。

上文所述,ONE BIN部分是有dd命令拼接而成,具体构成如下:

  • SPI-NAND部分:

    上图橙色的部分表示的是MTD PART中分区的大小,boot.bin表示实际做出来的one bin大小,紫色的部分代表pni中在这部分的layout。

    IPL/IPL_CUST的image一般不会超过一个block的size,因此它们和它们的Backup各自占用3个block的大小,这个大小是固定不变的。

    UBOOT的data占用了2个block的大小,由于uboot经常被客制化,uboot的image大小有可能会变化,因此配置2个block不能保证所有的情况,若要修改,请修改config文件中的:

    uboot$(DATASIZE) = 0x40000
    

4.3. SPINAND BOOT分区坏快处理

在UBOOT之上预留了4个BLOCK的GAP,这部分在config文件中是强制预留的,这4个BLOCK是为了充分利用IPL和IPL_CUST的BK BLK(它们各自有两个backup,加起来为4)。强制预留代码:

uboot$(PATSIZE) = $(call sum, $(uboot$(DATASIZE)) $(call multiplyhex, $(FLASH_BLK_SIZE), 4))

预留的目的是处理坏快的情况。从上图可知,MTD PART的BOOT分区比要烧录的boot.bin大4个block的大小,当使用tftp烧录或者用烧录器烧录代码的时候,若遇到坏快,数据会自动写到下一个block,那么在当前位置之后的数据都会整体位移:

假设在IPL_CUST DATA的位置发生坏快,那么烧录后如下所示:

在IPL_CUST后面的所有数据都位移了一个block,那么在uboot的data也会跟着位移一个block,GAP则会被占用一个block。在没有坏快的情况下,整个boot分区允许最大4个block的坏快,这4个坏块并不是任意位置的,其要满足在IPL/IPL_CUST分区中至少有一个block是好的。超过4个block或者不满足上述条件则会跳到BOOT1分区,保证能够顺利启动。


4.4. SPINAND的BL0 PBA/BL1 PBA标志位

使用工具SpiNandInfoEditor.exe打开flash.sni或者flash_list.sni后会看到如下的栏位:

这两个栏位是给rom code找IPL分区的,在ALKAID/project/board中找到的sni文件默认填的都是0,单位是block count,这表示rom code都是从默认的地址找IPL0和IPL1,默认的地址是IPL0: 0x140000IPL1:0x1A0000。显然跟当前分区配置是不匹配的,因此在cis.bin制作的过程中,会使用shell脚本把flash.sniflash_list.sni里面的BL0 PBA和BL1 PBA的填上正确的值,对应的就是BOOT0/BOOT1分区在flash上首地址对应的block offset。


4.5. SPINOR的BOOT分区

SPINOR目前默认没有做A/B分区,在BOOT的部分与SPINAND有区别,SPINOR的BOOT分区包含CIS,BOOT分区是从0地址开始的,BOOT分区的结构图如下图。

SPINOR由于不需要处理坏快,IPL、IPL_CUTS没有backup block,在boot部分的分区需要关注分区首地址、结束地址和分区大小对齐的问题,之所以以4k对齐,是为了节省空间。在boot中IPL/IPL_CUST/UBOOT/CIS分区首地址和分区大小都必须以一个nor flash的page对齐,page大小是4k,ENV分区不在BOOT分区里面,但是它要求ENV分区的结束地址需要以nor flash一个block的大小对齐,一般为64k,那么根据下图的layout,在UBOOT和ENV之间就会有一个GAP来保证这个对齐的要求。这个GAP与spinand的作用不一样,除了保证对齐要求之外,它还有一个功能,那就是应对UBOOT/IPL/IPL_CUS的data部分数据变化导致其在PNI内部分区变化所做的缓冲机制,其目的就是保证即使在PNI中这些分区变化了,但是MTD的BOOT分区和env分区是不变的。从而给ota升级BOOT时预留一个更大的变化量(主要原因是考虑到ota升级分区是无法变化的)。

NOR flash的分区中IPL/IPL_CUST/CIS/UBOOT的分区大小在脚本中都自动做了4k对齐,使用者只需要调整MTD的BOOT PARTITION的大小,从而起到增大或者减少GAP的效果。

BOOT的大小是有限制的,其大小加上ENV的分区大小必须以block size对齐,设定其大小的变量是:

boot$(PATSIZE) = 0x4F000


4.6. ONE BIN特别注意

ONE BIN的分区配置若需要修改,请先认真阅读本章节,仅可以适当修改spinand的uboot$(DATASIZE)spinor的boot$(PATSIZE)来应对IPL/IPL_CUS/UBOOT的image文件大小变化的情况,修改后请找FAE确认。

若一旦修改不当,容易造成生产环节BOOT无法启动的问题。

若打包流程未参照我司的ALKAID,特别注意,在spinand的sni_list以及gcis中的BL0 PBA和BL1 PBA需要根据当前的BOOT0/BOOT1位置填入正确的值,也可以在确认BOOT分区无异后,使用ALKAID编译出来的BOOT的partition layout以及boot.bin

若需要自行制作boot.bin,请参考本章节的说明,具体做法请参考脚本image.mk中的Makefile的函数updatecis,以及目标cis_nofsimage、boot_images