0%

君正linux ota升级方案调研

分区

^b2c15f

nand

1
2
3
4
5
6
7
8
9
10
11
[storageinfo]
mediumtype=nand
capacity=256MB
[partition]
# 分区名 偏移量 大小 设备名
item1=uboot,0x0,0x100000,mtdblock0
item2=nv,0x100000,0x100000,mtdblock1
item3=kernel,0x200000,0x800000,mtdblock2
item4=recovery,0xa00000,0x1000000,mtdblock3
item5=system,0x1a00000,0x5000000,mtdblock4
item6=userdata,0x6a00000,0x9300000,mtdblock5

升级主程序

也叫recovery

全局配置文件

1
2
3
4
5
6
7
struct global_data g_data = {
.public_key_path = "/usr/data/ota_res/key/key.pub",
.configure_file_path = "/usr/data/ota_res/recovery.conf",
.font_path = "/usr/data/ota_res/image/font.png",
.audio_path = "/usr/data/ota_res/tips",
.has_fb = 1,
};

配置文件示例:
recovery.conf

1
2
3
4
5
6
7
{
"server":{
"ip":"192.168.1.105",
"url":"https://192.168.1.105",
"rpt_url":""
}
}

类解析

configure_file 解析json 配置文件

1
2
3
4
5
6
7
8
9
10
struct configure_file {
void (*construct)(struct configure_file* this);
void (*destruct)(struct configure_file* this);
int (*parse)(struct configure_file* this, const char* path);
int (*parse_current_version)(struct configure_file* this);
void (*dump)(struct configure_file* this);
char *version;
char *server_ip;
char *server_url;
};

调用_new 方法调用了construct_configure_file 函数进行初始化, 安装了 parse dump parse_current_version的指针, 最终调用 parse_ota_configure 方法解析了 recovery.conf的内容, 关注的字段就是 ip url rpt_url 这三个, 最后把ip url 传递到了 configure_file 类的成员变量 server_ip, server_url

signal_handler 类管理注册捕捉信号

1
2
3
4
5
6
7
8
struct signal_handler {
void (*construct)(struct signal_handler *this);
void (*destruct)(struct signal_handler *this);
void (*set_signal_handler) (struct signal_handler* this,
int signal, signal_handler_t handler);

struct sigaction action;
};

调用_new 方法调用了construt_signal_handler 安装了 set_signal_handler 的函数指针
捕捉 SIGINT, SIGQUIT, SIGTERM 信号, 设置为 SIG_IGN, 忽略这些信号的处理

update_file 类

调用_new 方法调用了 construct_update_file 对其进行初始化, 安装了 create_sha1_table get_device_info_by_devtype 等的函数指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct update_file {
void (*construct)(struct update_file* this);
void (*destruct)(struct update_file* this);

int (*parse_device_xml)(struct update_file* this, const char* path,
struct device_info* device_info);
int (*parse_update_xml)(struct update_file* this, const char* path,
struct update_info* update_info);
int (*parse_global_xml)(struct update_file* this, const char* path);
int (*create_sha1_table)(struct update_file* this, const char* path);

void (*dump_update_info)(struct update_file* this,
struct update_info* update_info);
void (*dump_device_info)(struct update_file* this,
struct device_info* device_info);

struct update_info* (*get_update_info_by_devtype)(struct update_file* this,
const char* devtype);
struct device_info* (*get_device_info_by_devtype)(struct update_file* this,
const char* devtype);

const char** (*get_device_type_list)(struct update_file* this);
void (*dump_device_type_list)(struct update_file* this);

struct list_head device_list;
struct list_head update_list;
char** sha1_table;
int sha1_count;
};

net_interface 类 网络接口相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
struct net_interface {
void (*construct)(struct net_interface* this, const char* if_name);
void (*destruct)(struct net_interface* this);
int (*icmp_echo)(struct net_interface* this, const char* server_addr, int timeout);
int (*get_hwaddr)(struct net_interface* this, unsigned char *hwaddr);
int (*set_hwaddr)(struct net_interface* this, const unsigned char* hwaddr);
int (*get_addr)(struct net_interface* this, in_addr_t* addr);
int (*set_addr)(struct net_interface* this, in_addr_t addr);
int (*init_socket)(struct net_interface* this);
void (*close_socket)(struct net_interface* this);
int (*up)(struct net_interface* this);
int (*down)(struct net_interface* this);
cable_state_t (*get_cable_state)(struct net_interface* this);
int (*start_cable_detector)(struct net_interface* this, detect_listener_t listener, void* param);
void (*stop_cable_detector)(struct net_interface* this);
int(*ping)(const char *addr);

int socket;
int icmp_socket;
char* if_name;
detect_listener_t detect_listener;
void* detect_param;

cable_state_t cable_status;
int detect_pid;
bool detect_stopped;
bool detect_exit;
pthread_mutex_t detect_lock;
pthread_cond_t detect_cond;
};

调用 construct_net_interface 进行初始化, 安装函数指针, 这个函数主要是网络收发相关的

block manager 类解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
struct block_manager {
void (*construct)(struct block_manager* this, const char *blockname,
bm_event_listener_t listener, void* param);
void (*destruct)(struct block_manager* this);

void (*dump_event)(struct block_manager* this, struct bm_event* event);
void (*get_supported)(struct block_manager* this, char *buf);
void (*get_supported_filetype)(struct block_manager* this, char *buf);

int (*set_operation_option)(struct block_manager* this, struct bm_operation_option* opion,
int method, char *filetype);
struct bm_operate_prepare_info* (*prepare)(struct block_manager* this,
int64_t offset, int64_t length, struct bm_operation_option *option);
int (*chip_erase)(struct block_manager* this);
int64_t (*erase)(struct block_manager* this, int64_t offset,
int64_t length);
int64_t (*write)(struct block_manager* this, int64_t offset,
char* buf, int64_t length);
int64_t (*read)(struct block_manager* this, int64_t offset, char* buf,
int64_t length);
int (*format)(struct block_manager* this);

uint32_t (*get_prepare_io_size)(struct block_manager* this);
uint32_t (*get_prepare_leb_size)(struct block_manager* this);
int64_t (*get_prepare_write_start)(struct block_manager* this);
int64_t (*get_prepare_max_mapped_size)(struct block_manager* this);
int64_t (*finish)(struct block_manager* this);

int64_t (*get_partition_size_by_offset)(struct block_manager* this,
int64_t offset);
int64_t (*get_partition_size_by_name)(struct block_manager* this,
char *name);
int64_t (*get_partition_start_by_offset)(struct block_manager* this,
int64_t offset);
int64_t (*get_partition_start_by_name)(struct block_manager* this,
char *name);
int (*get_partition_count)(struct block_manager* this);
int64_t (*get_capacity)(struct block_manager* this);
int (*get_blocksize)(struct block_manager* this, int64_t offset);
int (*get_iosize)(struct block_manager* this, int64_t offset);
char* (*get_block_type)(struct block_manager* this, int64_t offset);
struct bm_operation_option operate_option;
struct bm_operate_prepare_info *prepared;
union bm_info desc;
struct bm_part_info *part_info;
#ifdef BM_SYSINFO_SUPPORT
struct sysinfo_manager *sysinfo;
#endif
char *name;
struct list_head list_cell;
struct list_head list_fs_head;
void* param;
};

调用了 construct_block_manager 方法对其初始化, 安装函数指针, 注册 bm_event_listener 回调,
设备层管理类, 是mtd类型的block.

这里需要重点看下 mtd_manager_init 的实现

mtd_block_init

这个函数主要是从/sys/class/mtd 下读取各个mtd_block 设备的信息, 填充到mtd_dev_info

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct mtd_dev_info
{
int mtd_num; //MTD device number
int major; // major number of corresponding character device
int minor; // minor number of corresponding character device
int type; //flash type (constants like %MTD_NANDFLASH defined in mtd-abi.h)
const char type_str[MTD_TYPE_MAX + 1]; // static R/O flash type string
const char name[MTD_NAME_MAX + 1]; // device name
long long size; // device size in bytes
int eb_cnt; // count of eraseblocks
int eb_size; // eraseblock size
int min_io_size; // minimum input/output unit size
int subpage_size; //sub-page size
int oob_size; // OOB size (zero if the device does not have OOB area)
int region_cnt; // count of additional erase regions
unsigned int writable:1; //zero if the device is read-only
unsigned int bb_allowed:1; // non-zero if the MTD device may have bad eraseblocks
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
+ mtd_block_init(struct block_manager* this)
\ - libmtd_open() "组装/sys/class/mtd/mtd%d 作为前缀的路径, 填充给mtd_desc"
| -+ mtd_get_info(*mtd_desc, mtd_info) "扫描出block_num 范围, 由highest_mtd_num lowest_mtd_num 界定"
\ - opendir(lib->sysfs_mtd) "/sys/class/mtd"
| -+ while
\ - readdir(sysfs_mtd) "/sys/class/mtd"
| - sscanf(dirent->d_name, "mtd%d""%s", &mtd_num, tmp_buf); "扫描出block_num"
| - info->mtd_dev_cnt += 1; "扫描一共多少个block设备"
| - info->highest_mtd_num = mtd_num; "扫描出block_num" 范围, 赋值给 highest_mtd_num
| - info->lowest_mtd_num = mtd_num; "扫描出block_num" 范围, 赋值给 lowest_mtd_num
| -+ for (i = 0; i < mtd_info->mtd_dev_cnt; i++) "遍历block 设备"
\ -+ mtd_get_dev_info1(*mtd_desc, i, mtd_dev_info)
\ - dev_get_major(lib, mtd_num, &mtd->major, &mtd->minor) "sprintf num填充libmtd_open函数组装好的%d占位代表了block_num的前缀, 获得dev的真实路径, /sys/class/mtd/mtd0/dev, 读取该路径, 获得major minor 号"
| - dev_read_data(lib->mtd_name, mtd_num, &mtd->name, MTD_NAME_MAX + 1); "跟上面过程一样, 获得name文件的内容, 填充到 mtd->name"
| - dev_read_data(lib->mtd_type, mtd_num, &mtd->type_str, MTD_TYPE_MAX + 1);
| - dev_read_pos_int(lib->mtd_eb_size, mtd_num, &mtd->eb_size)
| - dev_read_pos_ll(lib->mtd_size, mtd_num, &mtd->size) 从"/sys/class/mtd 下读取各个mtd_block 设备的信息, 填充到`mtd_dev_info ` bm->part_info[i].part.mtd_dev_info 中"
| - int* fd = BM_GET_PARTINFO_FD(this, i); "bm->part_info[i].fd"
| - path = BM_GET_PARTINFO_PATH(this, i); "(bm->part_info[i].path)"
| - sprintf(path, "%s%d", MTD_CHAR_HEAD, mtd_dev_info->mtd_num); "赋值 /dev/mtd/<block_num>"
| - *fd = open(path, O_RDWR); "打开 设备. 描述符保存到 bm->part_info[i].fd"
| - BM_GET_PARTINFO_START(this, i) = size; "保存该分区的起始位置 偏移量"

这一节的内容与 分区那一节是能对应上的

register_block_manager

将该block_manager的实例挂到 block_manager_list链表中, 通过block_manager的成员在链表中匹配查找

1
list_add_tail(&this->list_cell, &block_manager_list);

mtd_install_filesystem

注册文件系统, 注册了这几个文件系统, 将这几个文件系统的实例的 list_cell 挂到了 block_manager的 list_fs_head 成员的链表下. 并安装好了对应的函数指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct filesystem {
char *name;
struct list_head list_cell; // 挂入block_manager的 list_fs_head 的链表下
int (*init)(struct filesystem *fs);
int (*alloc_params)(struct filesystem *fs);
int (*free_params)(struct filesystem *fs);
void (*set_params)(struct filesystem* fs, char *buf, int64_t offset,
int64_t length, int op_method, void *p, void *fs_priv);
int (*format)(struct filesystem *fs);
int64_t (*erase)(struct filesystem *fs);
int64_t (*read)(struct filesystem *fs);
int64_t (*write)(struct filesystem *fs);
int64_t (*done)(struct filesystem *fs);
int64_t (*get_operate_start_address)(struct filesystem *fs);
unsigned long (*get_leb_size)(struct filesystem *fs);
int64_t (*get_max_mapped_size_in_partition)(struct filesystem *fs);
int tagsize;
unsigned int flag;
struct fs_operation_params *params;
void *priv;
};
1
2
3
4
5
6
7
static struct filesystem* fs_supported_list[] = {
&fs_normal,
&fs_jffs2,
&fs_ubifs,
&fs_yaffs2,
&fs_cramfs,
};

主升级管理类

ota_manager
调用 _new 方法调用了 ota_manget的 construct_ota_manager 方法进行初始化, 安装了 start stop load_configure 等的函数指针, 初始化了 net_interface 类, 调用了其构造方法, 初始化了 block_manager类, 调用了其构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
struct ota_manager {
void (*construct)(struct ota_manager* this);
void (*destruct)(struct ota_manager* this);
int (*start)(struct ota_manager* this);
int (*stop)(struct ota_manager* this);
void (*load_configure)(struct ota_manager* this, struct configure_file* cf);
void (*load_signal_handler)(struct ota_manager* this, struct signal_handler* sh);
struct block_manager* mtd_bm; // 保存了block_manager类的实例
struct configure_file* cf; //保存了configure_file 类的实例, 前面解析的参数都在该实例中
struct signal_handler* sh; //保存了 signal_hander 类的实例
struct update_file* uf; // 保存了 update_file 类的实例
struct net_interface* ni; //保存了net_interface类的实例
};

执行了 start 方法开启了 ota_manager, 这里是开了一个新线程, 用来执行升级任务
线程执行入口是main_task

升级线程

main_task
主要函数是 update_devices, 这个函数比较长, 这里拆成两个部分, 分为预升级部分和升级部分,
预升级部分主要是校验升级包, 解析设备中的分区信息, 解析升级包中的升级文件, 分析哪些分区以怎样的方式进行升级, 并将解析到的配置写到一个个记录升级相关的数据结构中

预升级部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
+ void *main_task(void* param)
\ -- struct ota_manager* this = (struct ota_manager*) param; "保存ota_manager的实例"
| -- this->sh->set_signal_handler(this->sh, SIGALRM, signal_handler); "设置了 `SIGALRM` 的信号处理函数为 ota_manager的 `signal_handler` 函数, 处理超时消息"
| -- alarm(ALARM_TIME_OUT); "设置超时线" 30*60 s, 如果升级时长超过30 min, alarm的signal_handler 会进行处理
| -- update_stages(UPDATE_START, NULL); "打印log 开始升级"
| -- this->uf = _new(struct update_file, update_file); "调用update_file构造函数, 安装对应的函数指针"
| -- ping_ota_server_success = try_ping_server(this, this->cf->server_ip); "ping 升级服务器. 是否能ping 通, 否则停止"
| -- this->ni->icmp_echo(this->ni, this->cf->server_ip, 30000) "发包, 检查服务器是否有回包, 无回包, 停止"
| -- this->cf->parse_current_version(this->cf); "解析 server ip url"
| -+ update_devices(this)
\ - creat_unzip_dir() "创建解压路径 /tmp/update"
| -+ download_and_create_sha1_table(this)
\ - download_file(path, prefix_local_update_path) "下载 server_url/sha1Tab 到 /tmp/update/ 下"
| - this->uf->create_sha1_table(this->uf, path) "update_file 类从sha1Tab 文件读出 sha_count个sha result entry, 保存到update_file 类的 sha1_table 数组中"
| -+ download_and_parse_global_conf(this) "global.xml 示例 <global><device type="opaque">nor</device></global>"
\ - download_file(path, prefix_local_update_path) "下载 server_url/global.xml 到 /tmp/update/ 下"
| -+ this->uf->parse_global_xml(this->uf, path) "解析global.xml 文件, 这一步主要是解析出block_device的flash 类型, 是nor nand mmc"
\ -+ for (node = mxmlFindElement(node, tree, "device")...
\ - const char* devtype = mxmlGetOpaque(node);
| - device_type_list[i++] = devtype;
| - strcpy(device_info->type, devtype);
| - list_add_tail(&device_info->head, &this->device_list); "挂入update_file的 device_list链表"
| - strcpy(update_info->devtype, devtype);
| - list_add_tail(&update_info->head, &this->update_list); "挂入update_file的 update_list链表"
| - const char** device_type_list = this->uf->get_device_type_list(this->uf);
| -+ for (int i = 0; device_type_list[i]; i++) "获取 parse_global_xml阶段挂入的device_list链表的数据"
\ - struct device_info* device_info = this->uf->get_device_info_by_devtype(this->uf, devtype);
| - struct update_info* update_info = this->uf->get_update_info_by_devtype(this->uf, devtype);
| -+ download_and_parse_update_conf(this, device_info, update_info, devtype)
\ - download_file(path, prefix_local_update_path) "下载server_url/<dev_type>/update000.zip 到/tmp/update/下"
| -+ check_device_update_info(this, path, device_info, update_info) "检查校验/tmp/update/update000.zip"
\ -+ check_pkg_sha1(this, path, 0)
\ - buff = "sha1sum path"
| - strncmp(buff, this->uf->sha1_table[index], 40) "与前面从sha1Tab 读出的count个entry的 第一个entry 比较是否一致, 一致表明 sha1 校验通过"
| -+ verify_update_pkg(this, path)
\ - load_keys(g_data.public_key_path, &nkeys) "加载rsa公钥 /usr/data/ota_res/key/key.pub, 公钥有nkeys个, 挨个试, 有一个能验签通过即可"
| -+ verify_file(path, keys, nkeys)
\ - fseek(f, -FOOTER_SIZE, SEEK_END) "读文件尾"
| - fread(footer, 1, FOOTER_SIZE, f) != FOOTER_SIZE) "读update000.zip的最后6个字节"
| - footer[2] != 0xff || footer[3] != 0xff) "校验 footer 第3 和 第4 字节" 如果不全为1, 校验失败
| - comment_size = footer[4] + (footer[5] << 8); "comment_size 第6个字节 high, 第5个字节low, 即16 字节"
| - eocd_size = comment_size + EOCD_HEADER_SIZE;
| - signature_start = footer[0] + (footer[1] << 8); "sinature_start 的位置"
| - fseek(f, -eocd_size, SEEK_END) "EOCD recorder 块"
| - signed_len = ftell(f) + EOCD_HEADER_SIZE - 2; "获取一共多少字节被签名了"
| - sha(f, 0, signed_len) "获取该文件的被签名保护的文件块的 hash"
| -+ for (i = 0; i < numKeys; ++i) "挨个pubkey 校验"
\ - RSA_verify(pKeys+i, eocd + eocd_size - 6 - RSANUMBYTES, RSANUMBYTES, sha1) "signature 存放的位置: eocd + eocd_size - 6 - RSANUMBYTES"

| -- unzip(path, prefix_local_update_path, NULL, 1) "zlib 的minizip 方案解压 update000.zip 到 /tmp/update/下"
| - sprintf(local_path, "%s/%s", prefix_local_update_path, prefix_device_xml); "/tmp/update/device.xml"
| -+ this->uf->parse_device_xml(this->uf, local_path, device_info) "解析device.xml", 查看示例 device.xml
\ -+ for (node = mxmlFindElement(node, tree, "item") ...
\ - struct part_info* partition = calloc(1, sizeof(struct part_info));
| - memcpy(partition->name, name, strlen(name));
| - partition->offset = strtoull(offset_str, NULL, 0);
| - partition->size = strtoull(size_str, NULL, 0);
| - memcpy(partition->block_name, block_name, strlen(block_name));
| - list_add_tail(&partition->head, &device_info->list); "partion的信息挂到 device_info->list下"
| -+ check_device_info(this, device_info) "仅支持nor 和 nand 类型的rom"
\ -+ list_for_each(pos, &device_info->list) "遍历device_info.xml中的 partition信息"
\ - offset = this->mtd_bm->get_partition_start_by_name(this->mtd_bm, blkname);
| - size = this->mtd_bm->get_partition_size_by_name(this->mtd_bm, blkname);
| - (offset != info->offset) || (size != info->size) "device_info.xml中的partition的信息与前面/sys/class/mtd下扫描出的各block_device即partition的信息进行比对, offset 和 size 一致才可以"
| -- sprintf(local_path, "%s/%s", prefix_local_update_path, prefix_update_xml); "/tmp/update/update.xml"
| -+ this->uf->parse_update_xml(this->uf, local_path, update_info) "解析升级包中的update.xml 查看示例 update.xml"
\ -+ for (node = mxmlFindElement(node, tree, "image")...)
\ - memcpy(image->name, name, strlen(name));
| - memcpy(image->fs_type, fs_type, strlen(fs_type));
| - image->offset = strtoull(offset_str, NULL, 0);
| - image->size = strtoull(size_str, NULL, 0);
| - image->update_mode = mxmlGetInteger(sub_node);
| - image->chunksize = mxmlGetInteger(sub_node);
| - image->chunkcount = mxmlGetInteger(sub_node);
| - list_add_tail(&image->head, &update_info->list); "解析各字段信息后以image为单位, 将升级信息挂入到update_info->list 链表"
| -+ merge_imginfo_into_partinfo(this, device_info, update_info)
\ -+ list_for_each(pos_devinfo, &device_info->list)
\ - part_info *part_info = list_entry(pos_devinfo, struct part_info, head); "在 parse_device_xml 阶段扫描出的各device下的所有的partition分区信息, 遍历"
| -+ list_for_each(pos_update, &update_info->list) "遍历所有待升级的分区镜像"
\ - image_info* image_info = list_entry(pos_update, struct image_info, head);
| - image_info->offset > part_info_left_boundary && image_info->offset <= part_info_right_boundary
"image_info的offset 落在 partition的 size 范围内, 说明该镜像是属于该分区的"
| - list_add_tail(&image_info->head_part, &part_info->list); "镜像挂入分区的链表"
| - part_info->image_count++; "一个分区有多个镜像, 这里的镜像可以理解为镜像的分片, 并不一定说明分区可以有多个镜像, 这里的image 概念是针对升级来说的"
| - part_info->total_chunks += image_info->chunkcount; "待升级的总chunk"

升级部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
+ update_devices(struct ota_manager* this)
\ -+ for (int i = 0; device_type_list[i]; i++)
\ - nv_read(sysinfo_flag_nv); sysinfo_flag_nv->start = SYSINFO_FLAG_NV_UPDATE_START; nv_write(sysinfo_flag_nv) "标记升级start信息, 写到nv 文件中"
| -+ update_images(this, device_info, update_info) "通过之前的预升级部分的解析, 已经将待升级的存储设备信息和升级信息填充到了device_info 和 update_info下"
\ - list_for_each(sub_pos, &update_info->list) chunk_count += info->chunkcount; "记录该存储设备的总的待升级的chunk数"
| -+ list_for_each(pos_devinfo, &device_info->list)
\ - struct part_info* part_info = list_entry(pos_devinfo, struct part_info, head); "遍历该存储设备上所有的分区"
| -+ if (part_info->image_count > 0) "如果该分区需要升级"
\ - struct image_info* image_info = list_entry(pos_imageinfo, struct image_info, head_part); "遍历该分区上所有待升级的镜像分片"
| - nv_read(sysinfo_flag_nv); index = sysinfo_flag_nv->step + 1; "nv文件记录升级index, 是待升级chunk的下标"
| -+ for (int j = 1; j <= image_info->chunkcount; j++)
\ - (path, "%s/%s/%s%03d.zip", this->cf->server_url, device_info->type, "update", chunk标记); "下载升级包"
| - download_file(path, prefix_local_update_path) "下载到/tmp/update/下"
| - check_pkg_sha1(this, path, index) "校验zip的sha1"
| - verify_update_pkg(this, path) "校验zip的签名, 预升级中已经介绍过该流程, 不赘述"
| - unzip(path, prefix_local_update_path, NULL, 1) "解压pakage"
| -+ update_package(this, update_info, part_info, image_info, path, j) "只支持nand 或nor"
\ - fd = open(path, O_RDWR); "打开升级包的package"
| - first_image = list_entry(part_info->list.next, struct image_info, head_part);
| - last_image = list_entry(part_info->list.prev, struct image_info, head_part);
| - bm->set_operation_option(bm, &option, BM_OPERATION_METHOD_PARTITION, image_info->fs_type) "chunk_index 为 1时 初始化"
| -+ bm_operate_prepare_info* prepare_info = bm->prepare(bm, image_info->offset, image_info->size, &option) "mtd_block_prepare"
\ - default_filetype = option->filetype;
| - fs = fs_get_registered_by_name(&this->list_fs_head, default_filetype) "根据待升级的镜像的文件系统类型得到对应的文件系统的实例指针"
| -+ prepare_convert_params(this, fs, offset, length, option);
\ - fs->set_params(fs, NULL, offset, length, op_method, mtd, this); "对应文件系统的实例设置升级参数"
| - mtd_get_prepare_info(this, fs, fs->get_operate_start_address(fs),
mtd_get_blocksize_by_offset(this, offset),
fs->get_leb_size(fs),
fs->get_max_mapped_size_in_partition(fs)); "填充 prepare_info 及 fs->params 相关结构"
| - write_buffer_size = bm->get_prepare_leb_size(bm); "获取前面填充的prepare_info信息"
| - write_media_leap = bm->get_blocksize(bm, image_info->offset);
| - write_buffer = malloc(write_buffer_size);
| - bm->erase(bm, image_info->offset, bm->get_partition_size_by_offset(bm, image_info->offset))
"擦除待升级镜像分片所在的[offset, offset+size] 区域"
| - cur_write_offset = bm->get_prepare_write_start(bm); "从prepare_info 中获取 fs->params->offset转化后的write_start 偏移"
| - char *buffer = write_buffer;
| -+ while(filesize)
\ - readsize = MIN(filesize, write_buffer_size);
| - read(fd, buffer + already_read, readsize - already_read) "读取升级包package的内容"
| -+ next_write_offset = bm->write(bm, cur_write_offset, write_buffer, readsize); "写入对应分区"
\ -+ fs = data_transfer_params_set(this, offset, buf, length)
\ - fs = bm->prepared->context_handle; "从prepare_info 信息找到 fs的实例"
| - fs->params->offset = offset; ... "设置fs write 相关的参数"
| - fs->write(fs);
| - filesize -= readsize;
| - cur_write_offset = next_write_offset;
| -+ !strcmp(last_image->name, image_info->name) && (chunk_index == image_info->chunkcount)) "判断实际完当前partition的条件"
\ -+ bm->finish(bm);
\ -+ mtd_block_finish(struct block_manager* this)
\ 文件系统 *fs = BM_GET_PREPARE_INFO_CONTEXT(this);
| - fs->done(fs);
| - sysinfo_flag_nv->step = index; nv_write(sysinfo_flag_nv) "持久化升级step"
| - set_process_info(this, BM_OPERATION_WRITE, index, chunk_count); "通知升级进度"
| - sysinfo_flag_nv->finish = SYSINFO_FLAG_NV_UPDATE_DONE; nv_write(sysinfo_flag_nv) "标记该存储设备已经升级结束, 写到nv文件中记录"

调用具体文件系统的方法进行更新

附录

device.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?xml version='1.0' encoding='utf-8'?>
<device capacity="0x10000000" type="nand">
<partition count="6">

<item>
<name type="opaque">uboot</name>
<offset>0x0</offset>
<size>0x100000</size>
<blockname type="opaque">mtdblock0</blockname>
</item>

<item>
<name type="opaque">nv</name>
<offset>0x100000</offset>
<size>0x100000</size>
<blockname type="opaque">mtdblock1</blockname>
</item>

<item>
<name type="opaque">kernel</name>
<offset>0x200000</offset>
<size>0x800000</size>
<blockname type="opaque">mtdblock2</blockname>
</item>

<item>
<name type="opaque">recovery</name>
<offset>0xa00000</offset>
<size>0x1000000</size>
<blockname type="opaque">mtdblock3</blockname>
</item>

<item>
<name type="opaque">system</name>
<offset>0x1a00000</offset>
<size>0x7600000</size>
<blockname type="opaque">mtdblock4</blockname>
</item>

<item>
<name type="opaque">userdata</name>
<offset>0x9000000</offset>
<size>0x6d00000</size>
<blockname type="opaque">mtdblock5</blockname>
</item>

</partition>
</device>

update.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?xml version='1.0' encoding='utf-8'?>
<update devcontrol="0x0" devtype="nand">
<imagelist count="3">
<image><name type="opaque">kernel_recovery</name>
<type type="opaque">normal</type>
<offset>0xa00000</offset>
<size>10571840</size>
<pkgidx>1</pkgidx>
<bootmode><type type="integer">0x0</type></bootmode>
<updatemode>
<pkgtype type="opaque">fullpkg</pkgtype>
<type type="integer">0x201</type>
<chunksize type="integer">1048576</chunksize>
<chunkcount type="integer">11</chunkcount>
</updatemode>
</image>

<image>
<name type="opaque">kernel</name>
<type type="opaque">normal</type>
<offset>0x200000</offset>
<size>4725200</size>
<pkgidx>12</pkgidx>
<bootmode><type type="integer">0xcc</type></bootmode>
<updatemode>
<pkgtype type="opaque">fullpkg</pkgtype>
<type type="integer">0x201</type>
<chunksize type="integer">1048576</chunksize>
<chunkcount type="integer">5</chunkcount>
</updatemode>
</image>

<image>
<name type="opaque">system.ubifs</name>
<type type="opaque">ubifs</type>
<offset>0x1a00000</offset>
<size>90025984</size>
<pkgidx>17</pkgidx>
<bootmode><type type="integer">0xcc</type></bootmode>
<updatemode><pkgtype type="opaque">fullpkg</pkgtype>
<type type="integer">0x201</type>
<chunksize type="integer">1015808</chunksize>
<chunkcount type="integer">89</chunkcount>
</updatemode>
</image>
</imagelist>
</update>

update 数据包结构

待升级三个分区, kernel recovery 和 system
每个分区对应的镜像分片信息记录在了 update.xml 中, 其中:

  • recovery的镜像名为 kernel_recovery 一共11个镜像分片
  • kernel 的镜像名为kernel, 一共5个镜像分片
  • system分区的镜像名为 system.ubifs, 一共89个镜像分片

update.xml中的 image->name 镜像名与分区的对应关系是以 image->offset 偏移与device.xml中part->offset 的值来映射的, image->offset落在[part->offset, part->offset+part->size ]范围内, 说明该镜像是属于这个part.

一共11+5+89 个update.zip 镜像分包, 加上update000.zip, 一共116个子包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
update000/update000/
├── device.xml
└── update.xml
update001/update001/
└── kernel_recovery_001
...
update011/update011/
└── kernel_recovery_011
update012/update012/
└── kernel_001
...
update016/update016/
└── kernel_005
update017/update017/
└── system.ubifs_001
...
update105/update105/
└── system.ubifs_089