1 /*
2 * Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 * Description: UPG ab mode c file
15 */
16
17 #include <stddef.h>
18 #include <stdint.h>
19 #include "errcode.h"
20 #include "common_def.h"
21 #include "sfc.h"
22 #include "partition.h"
23 #include "upg_debug.h"
24 #include "upg_porting.h"
25 #include "upg_ab.h"
26
27 #define UPG_AB_REGION_CONFIG_SIZE 0x1000
28 #define UPG_AB_DEFAULT_REGION 0
29 #define UPG_AB_CONFIG_CHECK 0x70746C6C
30
31 typedef struct {
32 uint32_t check_num; /* check number */
33 uint32_t run_region; /* run region */
34 } upg_ab_config_t;
35
36 typedef struct {
37 partition_information_t a_info;
38 partition_information_t b_info;
39 } upg_ab_region_config_t;
40
41 /**
42 * @brief 获取镜像分区配置参数
43 * @param region_cfg 传出的镜像分区配置参数
44 * @return 返回相关错误码
45 */
upg_get_region_config(upg_ab_region_config_t * region_cfg)46 static errcode_t upg_get_region_config(upg_ab_region_config_t *region_cfg)
47 {
48 errcode_t ret_val = ERRCODE_SUCC;
49 ret_val |= uapi_partition_get_info(PARTITION_APP_IMAGE, &(region_cfg->a_info));
50 ret_val |= uapi_partition_get_info(PARTITION_FOTA_DATA, &(region_cfg->b_info));
51 return ret_val;
52 }
53
54 /**
55 * @brief 获取镜像分区配置的地址
56 * @param region_cfg 传出的镜像分区配置参数
57 * @return 返回相关错误码
58 */
upg_get_ab_config_addr(uint32_t * addr)59 static errcode_t upg_get_ab_config_addr(uint32_t *addr)
60 {
61 partition_information_t b_info;
62 errcode_t ret;
63
64 ret = uapi_partition_get_info(PARTITION_FOTA_DATA, &b_info);
65 if (ret != ERRCODE_SUCC) {
66 return ERRCODE_FAIL;
67 }
68 *addr = b_info.part_info.addr_info.addr + b_info.part_info.addr_info.size - UPG_AB_REGION_CONFIG_SIZE;
69
70 return ERRCODE_SUCC;
71 }
72
73 /**
74 * @brief 初始化ab面升级方式的配置参数区。
75 * @param upg_cfg 需要返回的配置参数
76 * @return 返回相关错误码
77 */
upg_ab_config_init(void)78 static errcode_t upg_ab_config_init(void)
79 {
80 uint32_t upg_config_addr;
81 errcode_t ret = upg_get_ab_config_addr(&upg_config_addr);
82 if (ret != ERRCODE_SUCC) {
83 return ERRCODE_FAIL;
84 }
85
86 upg_ab_config_t upg_cfg = {
87 .check_num = UPG_AB_CONFIG_CHECK,
88 .run_region = UPG_AB_DEFAULT_REGION,
89 };
90
91 ret = upg_flash_erase(upg_config_addr, UPG_AB_REGION_CONFIG_SIZE);
92 ret |= upg_flash_write(upg_config_addr, sizeof(upg_ab_config_t), (const uint8_t *)(&upg_cfg), false);
93 if (ret != ERRCODE_SUCC) {
94 upg_log_err("[UPG] ab cfg init write failed, ret = 0x%x\r\n", ret);
95 return ERRCODE_UPG_FLASH_WRITE_ERROR;
96 }
97 return ERRCODE_SUCC;
98 }
99
100 /**
101 * @brief 获取ab面升级方式的配置参数。
102 * @param upg_cfg 需要返回的配置参数
103 * @return 返回相关错误码
104 */
upg_get_ab_config(upg_ab_config_t * upg_cfg)105 static errcode_t upg_get_ab_config(upg_ab_config_t *upg_cfg)
106 {
107 uint32_t upg_config_addr;
108 errcode_t ret = upg_get_ab_config_addr(&upg_config_addr);
109 if (ret != ERRCODE_SUCC) {
110 return ERRCODE_FAIL;
111 }
112
113 if (upg_flash_read(upg_config_addr, sizeof(upg_ab_config_t), (uint8_t *)upg_cfg) != ERRCODE_SUCC) {
114 return ERRCODE_FAIL;
115 }
116 if (upg_cfg->check_num != UPG_AB_CONFIG_CHECK) {
117 upg_log_err("[UPG] upg ab config check failed, will init, check_num = 0x%x\r\n", upg_cfg->check_num);
118 if (upg_ab_config_init() != ERRCODE_SUCC) {
119 return ERRCODE_FAIL;
120 }
121 }
122 return ERRCODE_SUCC;
123 }
124
125 /**
126 * @brief 设置ab面升级方式的配置参数。
127 * @param upg_cfg 需要设置的配置参数
128 * @return 返回相关错误码
129 */
130 #define UPG_SET_AB_CFG_TRY_MAX 3
upg_set_ab_config(upg_ab_config_t * upg_cfg)131 static errcode_t upg_set_ab_config(upg_ab_config_t *upg_cfg)
132 {
133 upg_ab_region_config_t region_cfg = {0};
134 uint32_t try_times = 0;
135 errcode_t ret = upg_get_region_config(®ion_cfg);
136 if (ret != ERRCODE_SUCC) {
137 return ERRCODE_FAIL;
138 }
139 uint32_t upg_config_addr = region_cfg.b_info.part_info.addr_info.addr + region_cfg.b_info.part_info.addr_info.size -
140 UPG_AB_REGION_CONFIG_SIZE;
141 while (try_times < UPG_SET_AB_CFG_TRY_MAX) {
142 ret = upg_flash_erase(upg_config_addr, UPG_AB_REGION_CONFIG_SIZE);
143 ret |= upg_flash_write(upg_config_addr, sizeof(upg_ab_config_t), (const uint8_t *)upg_cfg, false);
144 if (ret == ERRCODE_SUCC) {
145 break;
146 }
147 try_times++;
148 }
149 if (ret != ERRCODE_SUCC) {
150 upg_log_err("[UPG] upg set config failed, ret = 0x%x\r\n", ret);
151 return ERRCODE_UPG_FLASH_WRITE_ERROR;
152 }
153 return ERRCODE_SUCC;
154 }
155
156 /**
157 * @brief 获取当前运行的镜像分区。
158 * @return 成功返回分区序号,失败 默认从A区启动。
159 */
upg_get_run_region(void)160 upg_region_index upg_get_run_region(void)
161 {
162 upg_ab_config_t upg_cfg = {0};
163 if (upg_get_ab_config(&upg_cfg) != ERRCODE_SUCC) {
164 return UPG_AB_DEFAULT_REGION;
165 }
166 if (upg_cfg.run_region != UPG_REGION_A && upg_cfg.run_region != UPG_REGION_B) {
167 return UPG_AB_DEFAULT_REGION;
168 }
169 return upg_cfg.run_region;
170 }
171
172 /**
173 * @brief 获取待升级的镜像分区。
174 * @return 返回分区序号。
175 */
upg_get_upg_region(void)176 upg_region_index upg_get_upg_region(void)
177 {
178 if (upg_get_run_region() == UPG_REGION_B) {
179 return UPG_REGION_A;
180 } else {
181 return UPG_REGION_B;
182 }
183 }
184
185 /**
186 * @brief 获取镜像分区的起始地址。
187 * @param upg_region 需要获取的镜像分区
188 * @return 成功返回分区地址,失败返回 0
189 */
190 #define UPG_SIZE_4K_ALIGN 4095
191 #define UPG_SIZE_4K (UPG_SIZE_4K_ALIGN + 1)
upg_get_region_addr(upg_region_index upg_region)192 uint32_t upg_get_region_addr(upg_region_index upg_region)
193 {
194 upg_ab_region_config_t info = {0};
195 if (upg_get_region_config(&info) != ERRCODE_SUCC) {
196 upg_log_err("[UPG] upg get region addr failed\r\n");
197 return 0;
198 }
199 uint32_t image_addr = info.a_info.part_info.addr_info.addr;
200 uint32_t all_size = info.a_info.part_info.addr_info.size + info.b_info.part_info.addr_info.size;
201 uint32_t single_size = (all_size - UPG_AB_REGION_CONFIG_SIZE) / UPG_REGION_COUNT;
202 single_size = (single_size + UPG_SIZE_4K_ALIGN) & ~(UPG_SIZE_4K_ALIGN);
203 switch (upg_region) {
204 case UPG_REGION_A:
205 return image_addr;
206 case UPG_REGION_B:
207 return image_addr + single_size;
208 default:
209 return 0;
210 }
211 }
212
213 /**
214 * @brief 获取镜像分区的大小。
215 * @param upg_region 需要获取的镜像分区
216 * @return 成功返回分区大小,失败返回 0
217 */
upg_get_region_size(upg_region_index upg_region)218 uint32_t upg_get_region_size(upg_region_index upg_region)
219 {
220 unused(upg_region);
221 upg_ab_region_config_t info = {0};
222 if (upg_get_region_config(&info) != ERRCODE_SUCC) {
223 return 0;
224 }
225 uint32_t all_size = info.a_info.part_info.addr_info.size + info.b_info.part_info.addr_info.size;
226 uint32_t single_size = (all_size - UPG_AB_REGION_CONFIG_SIZE) / UPG_REGION_COUNT;
227 single_size = (single_size + UPG_SIZE_4K_ALIGN) & ~(UPG_SIZE_4K_ALIGN);
228 switch (upg_region) {
229 case UPG_REGION_A:
230 return single_size;
231 case UPG_REGION_B:
232 return all_size - UPG_AB_REGION_CONFIG_SIZE - single_size;
233 default:
234 return 0;
235 }
236 }
237
238 /**
239 * @brief 读取镜像数据。
240 * @param upg_region,镜像区域,UPG_REGION_XXX。
241 * @param offset 偏移量。
242 * @param buf 内存指针。
243 * @param len 读取的长度。
244 * @return 成功返回ERRCODE_SUCC,失败返回错误码。
245 */
upg_ab_image_read(upg_region_index upg_region,uint32_t offset,uint8_t * buf,size_t len)246 uint32_t upg_ab_image_read(upg_region_index upg_region, uint32_t offset, uint8_t *buf, size_t len)
247 {
248 uint32_t addr = upg_get_region_addr(upg_region);
249 uint32_t size = upg_get_region_size(upg_region);
250 if (addr == 0 || size == 0 || (offset + len) > size) {
251 return ERRCODE_INVALID_PARAM;
252 }
253 return upg_flash_read(addr + offset, len, buf);
254 }
255
256 /**
257 * @brief 写入镜像数据。
258 * @param upg_region,镜像区域,UPG_REGION_XXX。
259 * @param offset 偏移量。
260 * @param buf 待写入数据的内存指针。
261 * @param len 写入的长度。
262 * @return 成功返回ERRCODE_SUCC,失败返回错误码。
263 */
upg_ab_image_write(upg_region_index upg_region,uint32_t offset,uint8_t * buf,size_t len)264 uint32_t upg_ab_image_write(upg_region_index upg_region, uint32_t offset, uint8_t *buf, size_t len)
265 {
266 uint32_t addr = upg_get_region_addr(upg_region);
267 uint32_t size = upg_get_region_size(upg_region);
268 if (addr == 0 || size == 0 || (offset + len) > size) {
269 return ERRCODE_INVALID_PARAM;
270 }
271 /* false: start过程已擦除整个待升级镜像区,此处不需写前擦 */
272 return upg_flash_write(addr + offset, len, buf, false);
273 }
274
275 /**
276 * @brief 镜像版本切换
277 * @param upg_region 镜像区域,UPG_REGION_A/UPG_REGION_B。下一次启动的版本区域是upg_region。
278 * @return 成功返回ERRCODE_SUCC,失败返回错误码。
279 */
upg_set_run_region(upg_region_index upg_region)280 errcode_t upg_set_run_region(upg_region_index upg_region)
281 {
282 upg_ab_config_t upg_cfg = {0};
283 errcode_t ret = upg_get_ab_config(&upg_cfg);
284 if (ret != ERRCODE_SUCC) {
285 return ret;
286 }
287 if (upg_cfg.run_region == upg_region) {
288 upg_log_err("[UPG] set the same region with the current,please check\r\n");
289 return ERRCODE_INVALID_PARAM;
290 }
291 upg_cfg.run_region = upg_region;
292 ret = upg_set_ab_config(&upg_cfg);
293 if (ret != ERRCODE_SUCC) {
294 upg_log_err("[UPG] set the config failed\r\n");
295 return ret;
296 }
297 return ERRCODE_SUCC;
298 }
299
300 /**
301 * @brief 擦除待升级的镜像区
302 * @param region 待擦除的镜像区域,UPG_REGION_A/UPG_REGION_B。
303 * @return 成功返回ERRCODE_SUCC,失败返回错误码。
304 */
upg_region_erase(upg_region_index region)305 errcode_t upg_region_erase(upg_region_index region)
306 {
307 uint32_t addr = upg_get_region_addr(region);
308 uint32_t size = upg_get_region_size(region);
309 upg_log_info("[UPG] erase region addr:0x%08x, size:0x%08x\r\n", addr, size);
310
311 if ((addr & UPG_SIZE_4K_ALIGN) || (size % UPG_SIZE_4K)) {
312 upg_log_err("[UPG] region addr:0x%08x or size:0x%08x is not 4K aligned\r\n", addr, size);
313 return ERRCODE_INVALID_PARAM;
314 }
315 if (uapi_sfc_reg_erase(addr, size) != ERRCODE_SUCC) {
316 upg_log_err("[UPG] erase region failed\r\n");
317 return ERRCODE_FAIL;
318 }
319 return ERRCODE_SUCC;
320 }
321
322 /**
323 * @brief 镜像升级开始,准备写入镜像
324 * @param upg_region 镜像区域,UPG_REGION_A/UPG_REGION_B
325 * @return 成功返回ERRCODE_SUCC,失败返回错误码。
326 */
upg_ab_start(upg_region_index upg_region)327 errcode_t upg_ab_start(upg_region_index upg_region)
328 {
329 if (upg_region == UPG_REGION_A || upg_region == UPG_REGION_B) {
330 return ERRCODE_INVALID_PARAM;
331 }
332
333 if (ws63_upg_init() != ERRCODE_SUCC) {
334 return ERRCODE_FAIL;
335 }
336 return upg_region_erase(upg_get_upg_region());
337 }
338