MS-FLASHFS 文件系统
本章将介绍 MS-FLASHFS 文件系统相关的知识。
MS-FLASHFS 文件系统介绍
MS-FLASHFS 是翼辉信息自主研发的针对 MCU 内部 FLASH 的掉电安全文件系统,主要用于存放 APP 镜像和启动参数文件,支持 APP XIP(片内执行),和一般的片内执行文件系统(如 ROMFS)不同,MS-FLASHFS 支持文件的增、删、改和磁盘空间碎片整理功能,并且做到了掉电安全,可有效保障智能设备的 OTA 升级安全。
MS-FLASHFS 利用了外部文件系统备份功能,不需要在 MCU 内部 FLASH 划分两个 slot 这里参考了 mcuboot 的 slot 概念,可有效降低 OTA 升级功能对 MCU 内部 FLASH 容量的需求,大大降低智能设备的 MCU 成本(片外 FLASH 的成本要比片内 FLASH 的成本低得多)。
MS-FLASHFS 文件系统结构
MS-FLASHFS 为了掉电安全能力,MS-FLASHFS 需要使用 MCU 内部 FLASH 的两个扇区分别作为主文件系统表和从文件系统表,MS-FLASHFS DATA
区用于存放文件,MS-FLASHFS 设计时考虑到 MCU 内部 FLASH 扇区大小可能不相同的情况和为了提升内部 FLASH 的利用率,分配单元可以不是扇区,默认扇区最大可以有 16 个分配单元,每个扇区可以拥有不相同的分配单元数。
+-----------+--------------+---+------------+---+------------+---+-------------------+
| | | | | | | | |
| | | | MS-FLASHFS | | MS-FLASHFS | | |
| MS-BOOT | MS-RTOS | ~ | | ~ | | ~ | MS-FLASHFS DATA |
| | | | MAIN TBL | | SEC TBL | | |
| | | | | | | | |
+-----------+--------------+---+------------+---+------------+---+-------------------+
制作 MS-FLASHFS 镜像
可以使用 MS-RTOS ImagePackager 将 APP 镜像、启动参数文件等储存到 MS-FLASHFS 文件系统中并合并成为一个统一的 MCU FLASH 烧录镜像。
MS-RTOS ImagePackager 的使用方法见 MS-RTOS ImagePackager 解压缩目录下的《MS-RTOS ImagePackager.pdf》。
MS-FLASHFS 维护
MS-FLASHFS 维护指的是对 MS-FLASHFS 内的文件进行增、删、改和磁盘空间碎片整理。
在设备开发阶段,可以使用 MS-FLASHFS 相关的命令对 MS-FLASHFS 进行维护。
在设备正式运行阶段,可以通过 launcher 的 ms_apps_update
函数对 MS-FLASHFS 进行维护。
ms_err_t ms_apps_update(const char *update_req_path, const char *log_path);
ms_apps_update
函数需要升级请求文件和升级日志文件作为参数。
升级请求文件存储了升级动作文件的路径,而升级动作文件存储了需要对 MS-FLASHFS 进行的维护动作数组(以紧排方式存储)。
MS_STRUCT_PACK_BEGIN
struct ms_flashfs_action {
// 动作 id
#define MS_FLASHFS_ACTION_CREATE_FILE 0U // 创建文件
#define MS_FLASHFS_ACTION_UPDATE_FILE 1U // 升级文件
#define MS_FLASHFS_ACTION_REMOVE_FILE 2U // 删除文件
#define MS_FLASHFS_ACTION_DEFRAG 3U // 磁盘碎片整理
#define MS_FLASHFS_ACTION_UPDATE_OS 4U // 升级 OS 镜像(该类型动作由 MS-BOOT 执行)
MS_STRUCT_PACK_FIELD(ms_uint32_t id); // 动作 id
MS_STRUCT_PACK_FIELD(ms_uint32_t reserve_size); // 预留给未来升级的大小
MS_STRUCT_PACK_FIELD(ms_uint32_t crc32); // 文件的 CRC32
MS_STRUCT_PACK_FIELD(ms_err_t err); // 动作执行的错误码
MS_STRUCT_PACK_FIELD(char name[MS_FLASHFS_NAME_BUF_SIZE]); // 文件名
MS_STRUCT_PACK_FIELD(char source[MS_FLASHFS_PATH_BUF_SIZE]); // 外部文件系统中的源文件
} MS_STRUCT_PACK_STRUCT;
MS_STRUCT_PACK_END
typedef struct ms_flashfs_action ms_flashfs_action_t;
与 OTA 升级的对接
上层的 OTA 升级服务下载新版本 OS 镜像、APP 镜像、启动参数文件(这些文件可以是差分文件,也允许只升级部分文件)到外部文件系统后,在外部文件系统中生成升级动作文件和升级请求文件,然后调用 ms_rtos_update
函数,MS-RTOS 将文件系统 CACHE 回写到磁盘后进行重启。
MS-RTOS 重启后,在 BSP 中,一般由启动线程 boot_thread
来调用 ms_apps_update
函数:
static void boot_thread(ms_ptr_t arg)
{
// do something
ms_launcher_init(rsa_pub_key, rsa_pub_key_len);
ms_apps_update("/nor/update/firmware/update_req", "/nor/update/firmware/update_log");
// do something
}
ms_apps_update
函数按顺序执行升级动作文件中的每个动作,执行出错时会将错误码写到升级动作文件,并将升级日志记录到指定的日志文件中。
相关命令
命令 | 介绍 | 参数 |
---|---|---|
flashc | 拷贝外文件系统的文件进 MS-FLASHFS 中 | flashc name source_path [reserve_size(decimal)] |
flashr | 删除 MS-FLASHFS 中的一个文件 | flashr name |
flashu | 使用外文件系统的文件升级 MS-FLASHFS 中的一个文件 | flashu name |
flashd | 执行 MS-FLASHFS 磁盘碎片整理 | 无 |
flashl | 查看 MS-FLASHFS 中的文件信息 | 无 |
相关 API
// 注册 MS-FLASHFS 文件系统
ms_err_t ms_flashfs_register(void);
移植方法
// 分区的扇区大小表(扇区大小可以不同)
static const ms_uint32_t flash_sectors_size[] = {
128U * 1024U, /* FLASH_SECTOR_6 */
128U * 1024U, /* FLASH_SECTOR_7 */
128U * 1024U, /* FLASH_SECTOR_8 */
128U * 1024U, /* FLASH_SECTOR_9 */
};
// 分区信息
static ms_flashfs_partition_t flash_partition = {
.flash_base = ADDR_FLASH_SECTOR_6, // 分区的第一个扇区的地址
.n_sector = MS_ARRAY_SIZE(flash_sectors_size), // 分区有多少个扇区
.sectors_size = flash_sectors_size, // 提供扇区大小表
};
// 获得分区信息
static ms_err_t __stm32_flash_get_part_info(ms_ptr_t priv, const ms_flashfs_partition_t **part)
{
*part = &flash_partition;
return MS_ERR_NONE;
}
// 擦除指定的扇区
static ms_err_t __stm32_flash_erase_sector(ms_ptr_t priv, ms_uint32_t sector_id)
{
// sector_id 0 -> FLASH_SECTOR_6
// sector_id 1 -> FLASH_SECTOR_7
// sector_id 2 -> FLASH_SECTOR_8
// sector_id 3 -> FLASH_SECTOR_9
// do something
}
// 对指定地址进行编程
static ms_err_t __stm32_flash_program(ms_ptr_t priv, ms_addr_t addr, ms_const_ptr_t buf, ms_uint32_t len)
{
// do something
}
// 擦除指定的文件系统表并编程
static ms_err_t __stm32_flash_erase_program_tbl(ms_ptr_t priv, ms_addr_t tbl_addr, ms_const_ptr_t buf, ms_uint32_t len)
{
// do something
}
// 无效指定的文件系统表
static ms_err_t __stm32_flash_invalid_tbl(ms_ptr_t priv, ms_addr_t tbl_addr)
{
// 文件系统表第一个字写为 0 则可以无效文件系统表
ms_uint32_t data = 0;
return __stm32_flash_program(priv, tbl_addr, &data, sizeof(data));
}
// 定义 MS-FLASHFS 驱动
static ms_flashfs_drv_t stm32_flash_drv = {
.get_part_info = __stm32_flash_get_part_info,
.erase_sector = __stm32_flash_erase_sector,
.program = __stm32_flash_program,
.erase_program_tbl = __stm32_flash_erase_program_tbl,
.invalid_tbl = __stm32_flash_invalid_tbl,
};
// 挂载 MS-FLASHFS
ms_err_t stm32_flash_mount(const char *mnt_path)
{
// 挂载参数
ms_flashfs_mount_param_t mount_param;
mount_param.main_tbl_addr = BSP_CFG_FLASHFS_MAIN_TBL_ADDR; // 主文件系统表扇区地址
mount_param.sec_tbl_addr = BSP_CFG_FLASHFS_SEC_TBL_ADDR; // 从文件系统表扇区地址
mount_param.drv = &stm32_flash_drv; // MS-FLASHFS 驱动
mount_param.priv = MS_NULL; // 私有信息
mount_param.readonly = MS_FALSE; // 是否只读
mount_param.calc_crc32 = ms_crc32; // 提供 CRC32 函数
mount_param.mkfs_param.max_file = BSP_CFG_FLASHFS_MAX_FILE; // 如果没有格式化,则用这参数进行格式化,最大存放多少个文件
mount_param.mkfs_param.unit_size = BSP_CFG_FLASHFS_UNIT_SIZE; // 如果没有格式化,则用这参数进行格式化,分配单元的大小(字节单位)
mount_param.update_req_path = MS_NULL;
return ms_io_mount(mnt_path, MS_NULL, MS_FLASHFS_NAME, &mount_param);
}
static void boot_thread(ms_ptr_t arg)
{
// do something
// 注册 MS-FLASHFS 文件系统
ms_flashfs_register();
// 挂载到 /flash 目录
stm32_flash_mount("/flash");
// do something
}