ubi 分区
这边只记录实战,其他博客上写的很常见的内容就不介绍啦。
通常介绍的ubi使用都是例如左图中的方法,也就是flash设备,并不是所有的空间都被格式化为ubi的分区。仍有其他文件系统直接在mtd分区之上。而右图中,所有的flash都被格式化为ubi分区。而在ubi之上,会有多个卷,这才是呈现给用户的分区。但是实际上只有一个mtd。也就是说用户看到的分区,不直接是mtd的分区。这样的好处是:
- UBI 系统提供了基于大的MTD 分区的坏块管理和擦写均衡
- 基于 UBI Volume 的升级接口,由 UBI 系统管理坏块,实现容易
- UBI Volume 的大小可以动态调整,分区调整的更加容易
uboot环境变量设置
#define CONFIG_BOOTARGS "console=ttyAMA0,115200n8 ubi.mtd=1 ubi.mtd=2 rootfstype=ubifs"
#define CONFIG_BOOTCOMMAND "bootm 0x82000000"
#define CONFIG_MEM_LINUXOS_SIZE "128M"
#define CONFIG_MTDPART "hinand:2M(boot),75M(main),75M(backup),100M(data)"
以上是非常关键的信息,解释如下:
- ubi.mtd=1 ubi.mtd=2,这里之所以有mtd1,2是根据ubi的规则,kernel将会将这两个ubi区域,进行挂载。也就是说这里没有写的mtd区域,kernel是没有办法直接进行挂载的。
这里初次接触的时候,其实还是有点难以理解。因为在uboot中就制定了ubi和那个mtd分区关联。这个时候kernel还没有跑起来,都不知道有那几个分区,如何关联。这个其实是这样理解的,因为如何分区,如何规划,应在CONFIG_MTDPART 中约定好了,kernel起来之后必定是按照这个开始执行。因为镜像我们已经提前按照分区表下载到了flash中 - bootm 0x82000000,这里并没有看到将kernel进行拷贝的命令,后面会解释,这里不需要拷贝。因为已经提前通过uboot的ubi的接口加载到了内存当中。
- CONFIG_MTDPART ,这个是真正的mtd的分区,但不是用户看到的分区,因为会在大的mtd的分区之上建立卷。
用户镜像制作
run "${MKUBIFS} -F -d ${rootfs_dir} -m ${pagesize} -o ${rootfs_img} -e ${LEB} -c ${MAX_LEB_CNT_ROOTFS}"
run "${MKUBIFS} -F -d ${userdata_dir} -m ${pagesize} -o ${userdata_img} -e ${LEB} -c ${MAX_LEB_CNT_USERDATA}"
run "${MKUBIFS} -F -d ${configb_dir} -m ${pagesize} -o ${configb_img} -e ${LEB} -c ${MAX_LEB_CNT_CONFIGB}"
{
echo "[kernel]"
echo "mode=ubi"
echo "image=${rootdir}/image/ubifs/kernel.images"
echo "vol_id=0"
echo "vol_size=4092KiB"
echo "vol_type=static"
echo "vol_alignment=1"
echo "vol_name=kernel"
echo ""
echo "[rootfs]"
echo "mode=ubi"
echo "image=${rootdir}/image/ubifs/rootfs_128k.ubiimg"
echo "vol_id=1"
echo "vol_size=68200KiB"
echo "vol_type=dynamic"
echo "vol_alignment=1"
echo "vol_name=rootfs"
echo ""
echo "[flaga]"
echo "mode=ubi"
echo "image=${rootdir}/image/ubifs/flagdata.bin"
echo "vol_id=2"
echo "vol_size=124KiB"
echo "vol_type=dynamic"
echo "vol_alignment=1"
echo "vol_name=flaga"
echo ""
echo "[flagb]"
echo "mode=ubi"
echo "image=${rootdir}/image/ubifs/flagdata.bin"
echo "vol_id=3"
echo "vol_size=124KiB"
echo "vol_type=dynamic"
echo "vol_alignment=1"
echo "vol_name=flagb"
echo ""
} > ${ubicfg}
run "${MKUBI} -o ${ubifsimg} -m ${pagesize} -p ${blocksize} ${ubicfg}"
这里介绍了在一个大的ubi分区上如何建立卷。实现多个文件系统。
uboot实现kernel双备份
//一个ubi分区中,卷的描述信息,根据这个可以扫描到对应的卷
static vol_scan_rec_t st_scan_rec_arr[UBI_MAX_SCAN_VOL] = {
[0] = {
.vol_id = KERNEL_VOL_ID,
.max_lebs = KERNEL_MAX_LEBS,
.map_arr = map_arr_rec_0,
.sqnum_arr = sqnum_arr_rec_0,
.leb_cnt = 0
},
[1] = {
.vol_id = LAYOUT_VOL_ID,
.max_lebs = LAYOUT_MAX_LEBS,
.map_arr = map_arr_rec_1,
.sqnum_arr = sqnum_arr_rec_1,
.leb_cnt = 0
},
[2] = {
.vol_id = FLAGA_VOL_ID,
.max_lebs = FLAGA_MAX_LEBS,
.map_arr = map_arr_rec_2,
.sqnum_arr = sqnum_arr_rec_2,
.leb_cnt = 0
},
[3] = {
.vol_id = FLAGB_VOL_ID,
.max_lebs = FLAGB_MAX_LEBS,
.map_arr = map_arr_rec_3,
.sqnum_arr = sqnum_arr_rec_3,
.leb_cnt = 0
},
[4] = {
.vol_id = KERNEL_VOL_ID,
.max_lebs = KERNEL_MAX_LEBS,
.map_arr = map_arr_rec_4,
.sqnum_arr = sqnum_arr_rec_4,
.leb_cnt = 0
},
}
//拷贝kernel到内存中地址
#define HI_CFG_MEM_RESV_HIGH 0x82000000
int fh_read_kernel(unsigned int flag)
{
if (0 == is_main_scanned)
{
if (0 != fh_boot_ubi_scan(0))
return HI_RET_FAIL;
}
if (0 == flag)
{
if (0 != main_upd_marker_arr[KERNEL_VOL_ID])
return HI_RET_FAIL;
if (0 != ubi_read_kernel(&st_scan_rec_arr[0], (char *)HI_CFG_MEM_RESV_HIGH))
return HI_RET_FAIL;
}
else
{
if (0 == is_bkup_scanned)
{
if (0 != fh_boot_ubi_scan(1))
return HI_RET_FAIL;
}
if (0 != bkup_upd_marker_arr[KERNEL_VOL_ID])
return HI_RET_FAIL;
if (0 != ubi_read_kernel(&st_scan_rec_arr[4], (char *)HI_CFG_MEM_RESV_HIGH))
return HI_RET_FAIL;
}
}
void autoboot_command(const char *s)
{
char *pc_buf = NULL;
unsigned int ui_flag = 0;
unsigned int ui_cnt = 0;
char c_hint[64];
debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
for ( ui_cnt = 0; ui_cnt < 2; ui_cnt++ )
{
if( 1 == ui_cnt)
fh_set_bootstate( FH_KERNEL_FLAG, (ui_flag ? FH_SET_FLAGB : FH_SET_FLAGA) );
if ( 0 == ui_flag )
sprintf( c_hint, "\nBoot from %s...","kernelA" );
else
sprintf( c_hint, "\nBoot from %s...","kernelB" );
// 这里已经将kernel,读取到内存中去了,所以bootm哪里没有读取命令
if (0 != fh_read_kernel(ui_flag ? 1 : 0))
{
ui_flag = !ui_flag;
continue;
}
debug("\n %s\n",c_hint);
run_command_list(s, -1, 0);
}
}
需要注意的问题,这里为什么一定要用fh_read_kernel去读取内核
原因是,这里使用ubi文件系统,并不是直接将kernel镜像写进flash。可以理解为通过ubi将镜像写进flash。所以使用的时候同样需要通过ubi将镜像读出来。
kernel实现rootfs双备份
这里和镜像制作中的内容是一一对应的。kernel挂载ubi0和ubi1中的rootfs。
static int fhdrv_kdrv_rootfs_roll_back(unsigned char rfs_st, char *fs, int flags, void *data)
{
int err = 0;
printk(KERN_INFO "rootfs_roll_back parm: rfs_st=%c.\n", rfs_st);
if (0 == strcmp(fs, "ubifs"))
{
if('0' == rfs_st)
{
printk(KERN_INFO "rootfs trying roll back ubi1:rootfs.\n");
err = sys_mount("ubi1:rootfs", "/root", fs, flags, data);
}
else
{
printk(KERN_INFO "rootfs trying roll back ubi0:rootfs.\n");
err = sys_mount("ubi0:rootfs", "/root", fs, flags, data);
}
}
if(err)
return err;
printk(KERN_INFO "rootfs mount roll back %c.\n", (rfs_st == '1' ? '0' : '1'));
fhdrv_kdrv_set_rootfs_bootstate(rfs_st);
return 0;
}
实际使用的分区
uboot中升级
使用flash命令操作,会破坏卷。因为单个分区进行升级,需要使用ubi的命令。使用烧片镜像就可以使用flash操作命令进行升级。