From 3bb48e0448e1480d3411285562ee4d7c6de472a9 Mon Sep 17 00:00:00 2001 From: Pim van Pelt Date: Sun, 29 Sep 2019 21:09:52 +0200 Subject: [PATCH] Retire old fork of OTA --- libs/ota-common/LICENSE | 13 - .../include/cc3200/cc3200_updater.h | 31 - libs/ota-common/include/esp32/esp32_updater.h | 30 - libs/ota-common/include/mgos_updater.h | 47 - libs/ota-common/include/mgos_updater_common.h | 132 --- libs/ota-common/include/mgos_updater_hal.h | 108 --- libs/ota-common/include/mgos_updater_util.h | 22 - libs/ota-common/mos.yml | 34 - libs/ota-common/mos_stm32.yml | 2 - libs/ota-common/src/cc3200/cc3200_updater.c | 505 ---------- libs/ota-common/src/esp32/esp32_updater.c | 645 ------------- libs/ota-common/src/esp8266/esp_updater.c | 481 ---------- libs/ota-common/src/mgos_updater_common.c | 887 ------------------ libs/ota-common/src/stm32/stm32_updater.c | 370 -------- libs/ota-http-client/LICENSE | 14 - libs/ota-http-client/README.md | 26 - .../include/mgos_ota_http_client.h | 36 - libs/ota-http-client/mjs_fs/api_ota.js | 6 - libs/ota-http-client/mos.yml | 31 - .../src/mgos_ota_http_client.c | 260 ----- libs/rpc-service-ota/LICENSE | 14 - libs/rpc-service-ota/README.md | 89 -- .../include/mgos_rpc_service_ota.h | 38 - libs/rpc-service-ota/mos.yml | 17 - .../src/mgos_rpc_service_ota.c | 329 ------- 25 files changed, 4167 deletions(-) delete mode 100644 libs/ota-common/LICENSE delete mode 100644 libs/ota-common/include/cc3200/cc3200_updater.h delete mode 100644 libs/ota-common/include/esp32/esp32_updater.h delete mode 100644 libs/ota-common/include/mgos_updater.h delete mode 100644 libs/ota-common/include/mgos_updater_common.h delete mode 100644 libs/ota-common/include/mgos_updater_hal.h delete mode 100644 libs/ota-common/include/mgos_updater_util.h delete mode 100644 libs/ota-common/mos.yml delete mode 100644 libs/ota-common/mos_stm32.yml delete mode 100644 libs/ota-common/src/cc3200/cc3200_updater.c delete mode 100644 libs/ota-common/src/esp32/esp32_updater.c delete mode 100644 libs/ota-common/src/esp8266/esp_updater.c delete mode 100644 libs/ota-common/src/mgos_updater_common.c delete mode 100644 libs/ota-common/src/stm32/stm32_updater.c delete mode 100644 libs/ota-http-client/LICENSE delete mode 100644 libs/ota-http-client/README.md delete mode 100644 libs/ota-http-client/include/mgos_ota_http_client.h delete mode 100644 libs/ota-http-client/mjs_fs/api_ota.js delete mode 100644 libs/ota-http-client/mos.yml delete mode 100644 libs/ota-http-client/src/mgos_ota_http_client.c delete mode 100644 libs/rpc-service-ota/LICENSE delete mode 100644 libs/rpc-service-ota/README.md delete mode 100644 libs/rpc-service-ota/include/mgos_rpc_service_ota.h delete mode 100644 libs/rpc-service-ota/mos.yml delete mode 100644 libs/rpc-service-ota/src/mgos_rpc_service_ota.c diff --git a/libs/ota-common/LICENSE b/libs/ota-common/LICENSE deleted file mode 100644 index 66bb85a..0000000 --- a/libs/ota-common/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2018 Cesanta Software Limited - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/libs/ota-common/include/cc3200/cc3200_updater.h b/libs/ota-common/include/cc3200/cc3200_updater.h deleted file mode 100644 index b842055..0000000 --- a/libs/ota-common/include/cc3200/cc3200_updater.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2014-2018 Cesanta Software Limited - * All rights reserved - * - * Licensed under the Apache License, Version 2.0 (the ""License""); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an ""AS IS"" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -bool cc3200_upd_init(void); -const char *cc3200_upd_get_fs_container_prefix(void); - -#ifdef __cplusplus -} -#endif diff --git a/libs/ota-common/include/esp32/esp32_updater.h b/libs/ota-common/include/esp32/esp32_updater.h deleted file mode 100644 index 9c069cd..0000000 --- a/libs/ota-common/include/esp32/esp32_updater.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2014-2018 Cesanta Software Limited - * All rights reserved - * - * Licensed under the Apache License, Version 2.0 (the ""License""); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an ""AS IS"" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -void esp32_updater_early_init(); - -#ifdef __cplusplus -} -#endif diff --git a/libs/ota-common/include/mgos_updater.h b/libs/ota-common/include/mgos_updater.h deleted file mode 100644 index 171fec7..0000000 --- a/libs/ota-common/include/mgos_updater.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -/* - * OTA API. - * - * See https://mongoose-os.com/docs/mos/userguide/ota.md for more details about - * Mongoose OS OTA mechanism. - */ - -#ifndef CS_FW_SRC_MGOS_UPDATER_H_ -#define CS_FW_SRC_MGOS_UPDATER_H_ - -#include - -#include "frozen.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -struct mgos_upd_file_info { - char name[50]; - uint32_t size; - uint32_t processed; -}; - -struct mgos_upd_info { - /* Data from the manifest, available from BEGIN until END */ - struct json_token name; - struct json_token platform; - struct json_token version; - struct json_token build_id; - struct json_token parts; - bool abort; /* If MGOS_EVENT_OTA_BEGIN handler sets this to true, abort OTA */ - - /* Current file, available in PROGRESS. */ - struct mgos_upd_file_info current_file; -}; - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CS_FW_SRC_MGOS_UPDATER_H_ */ diff --git a/libs/ota-common/include/mgos_updater_common.h b/libs/ota-common/include/mgos_updater_common.h deleted file mode 100644 index 35da50a..0000000 --- a/libs/ota-common/include/mgos_updater_common.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - * - * Common bits of code handling update process. - * Driven externaly by data source - mg_rpc or POST file upload. - */ - -#ifndef CS_FW_SRC_MGOS_UPDATER_COMMON_H_ -#define CS_FW_SRC_MGOS_UPDATER_COMMON_H_ - -#include -#include - -#include "frozen.h" -#include "mgos_event.h" -#include "mgos_timers.h" -#include "mgos_updater.h" -#include "mgos_updater_hal.h" -#include "mongoose.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -struct update_context; -typedef void (*mgos_updater_result_cb)(struct update_context *ctx); - -struct mgos_upd_hal_ctx; /* This struct is defined by HAL and is opaque to us */ - -#define MGOS_EVENT_OTA_BASE MGOS_EVENT_BASE('O', 'T', 'A') -enum mgos_event_ota { - MGOS_EVENT_OTA_BEGIN = - MGOS_EVENT_OTA_BASE, /* ev_data: struct mgos_upd_info */ - MGOS_EVENT_OTA_STATUS, /* ev_data: struct mgos_ota_status */ - MGOS_EVENT_OTA_REQUEST, /* ev_data: struct ota_request_param */ -}; - -enum mgos_ota_state { - MGOS_OTA_STATE_IDLE = 0, /* idle */ - MGOS_OTA_STATE_PROGRESS, /* "progress" */ - MGOS_OTA_STATE_ERROR, /* "error" */ - MGOS_OTA_STATE_SUCCESS, /* "success" */ -}; - -struct mgos_ota_status { - bool is_committed; - int commit_timeout; - int partition; - enum mgos_ota_state state; - const char *msg; /* stringified state */ - int progress_percent; /* valid only for "progress" state */ -}; - -const char *mgos_ota_state_str(enum mgos_ota_state state); - -struct update_context { - int update_state; /* Internal state machine - parsing zip, etc */ - enum mgos_ota_state ota_state; /* Externally visible */ - const char *status_msg; - - char *zip_file_url; - size_t zip_file_size; - size_t bytes_already_downloaded; - size_t last_reported_bytes; - double last_reported_time; - - const char *data; - size_t data_len; - struct mbuf unprocessed; - - struct mgos_upd_info info; - uint32_t current_file_crc; - uint32_t current_file_crc_calc; - bool current_file_has_descriptor; - - bool ignore_same_version; - bool need_reboot; - - int result; - mgos_updater_result_cb result_cb; - - char *manifest_data; - char file_name[50]; - - struct mgos_upd_hal_ctx *dev_ctx; - - mgos_timer_id wdt; - int wdt_timeout_ms; - - /* Network connection associated with this update, if any. - * It is only used in case update times out - it is closed. */ - struct mg_connection *nc; - - /* - * At the end of update this struct is written to a file - * and then restored after reboot. - */ - struct update_file_context { - int commit_timeout; - } fctx __attribute__((packed)); -}; - -struct update_context *updater_context_create(int timeout); - -/* - * Returns updater context of the update in progress. If no update is in - * progress, returns NULL. - */ -struct update_context *updater_context_get_current(void); -int updater_process(struct update_context *ctx, const char *data, size_t len); -void updater_finish(struct update_context *ctx); -void updater_context_free(struct update_context *ctx); -int updater_finalize(struct update_context *ctx); -int is_write_finished(struct update_context *ctx); -int is_update_finished(struct update_context *ctx); -int is_reboot_required(struct update_context *ctx); - -void mgos_upd_boot_finish(bool is_successful, bool is_first); -bool mgos_upd_commit(); -bool mgos_upd_is_committed(); -bool mgos_upd_revert(bool reboot); -bool mgos_upd_get_status(struct mgos_ota_status *); - -int mgos_upd_get_commit_timeout(); -bool mgos_upd_set_commit_timeout(int commit_timeout); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CS_FW_SRC_MGOS_UPDATER_COMMON_H_ */ diff --git a/libs/ota-common/include/mgos_updater_hal.h b/libs/ota-common/include/mgos_updater_hal.h deleted file mode 100644 index dbf4b23..0000000 --- a/libs/ota-common/include/mgos_updater_hal.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - * - * Updater HAL. Devices need to implement this interface. - */ - -#ifndef CS_FW_SRC_MGOS_UPDATER_HAL_H_ -#define CS_FW_SRC_MGOS_UPDATER_HAL_H_ - -#include - -#include "common/mbuf.h" -#include "common/mg_str.h" -#include "frozen.h" - -#include "mgos_updater.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -struct mgos_upd_hal_ctx *mgos_upd_hal_ctx_create(void); - -const char *mgos_upd_get_status_msg(struct mgos_upd_hal_ctx *ctx); - -/* - * Process the firmware manifest. Parsed manifest is available in ctx->manifest, - * and for convenience the "parts" key within it is in ctx->parts. - * Return >= 0 if ok, < 0 + ctx->status_msg on error. - */ -int mgos_upd_begin(struct mgos_upd_hal_ctx *ctx, struct json_token *parts); - -/* - * Decide what to do with the next file. - * In case of abort, message should be provided in status_msg. - */ -enum mgos_upd_file_action { - MGOS_UPDATER_ABORT, - MGOS_UPDATER_PROCESS_FILE, - MGOS_UPDATER_SKIP_FILE, -}; -enum mgos_upd_file_action mgos_upd_file_begin( - struct mgos_upd_hal_ctx *ctx, const struct mgos_upd_file_info *fi); - -#ifndef MGOS_UPDATER_DATA_CHUNK_SIZE -#define MGOS_UPDATER_DATA_CHUNK_SIZE 512 -#endif -/* - * Process a chunk of file data. Data will be delivered to this function in - * MGOS_UPDATER_DATA_CHUNK_SIZE chunks. - * Return number of bytes processed (0 .. data.len) - * or < 0 for error. In case of error, message should be provided in status_msg. - */ -int mgos_upd_file_data(struct mgos_upd_hal_ctx *ctx, - const struct mgos_upd_file_info *fi, struct mg_str data); - -/* - * Finalize a file. Remainder of the data (if any) is passed, - * number of bytes of that data processed should be returned. The amount of data - * will be less than MGOS_UPDATER_DATA_CHUNK_SIZE. - * Value equal to data.len is an indication of success, - * < 0 + ctx->status_msg on error. - */ -int mgos_upd_file_end(struct mgos_upd_hal_ctx *ctx, - const struct mgos_upd_file_info *fi, struct mg_str data); - -/* - * Finalize the update. - * Return >= 0 if ok, < 0 + ctx->status_msg on error. - */ -int mgos_upd_finalize(struct mgos_upd_hal_ctx *ctx); - -bool mgos_upd_is_first_boot(void); - -void mgos_upd_hal_ctx_free(struct mgos_upd_hal_ctx *ctx); - -/* Apply update on first boot, usually involves merging filesystem. */ -int mgos_upd_apply_update(void); - -/* - * Create a snapshot of currently running firmware (including FS) in - * a currently inactive slot. There must be no uncommitted update - * in progress. - * Returns slot id used for snapshot or < 0 in case of error. - */ -int mgos_upd_create_snapshot(void); - -struct mgos_upd_boot_state { - /* Slot that will be used to load firmware during next boot. */ - int active_slot; - /* Whether the boot configuration is committed or not. - * Reboot with uncommitted configration reverts to revert_slot. */ - bool is_committed; - /* Slot that will be used in case of revert, explicit or implicit. */ - int revert_slot; -}; -bool mgos_upd_boot_get_state(struct mgos_upd_boot_state *bs); -bool mgos_upd_boot_set_state(const struct mgos_upd_boot_state *bs); -/* Shortcuts for get and set */ -void mgos_upd_boot_commit(void); -void mgos_upd_boot_revert(void); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CS_FW_SRC_MGOS_UPDATER_HAL_H_ */ diff --git a/libs/ota-common/include/mgos_updater_util.h b/libs/ota-common/include/mgos_updater_util.h deleted file mode 100644 index 741b61e..0000000 --- a/libs/ota-common/include/mgos_updater_util.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2014-2016 Cesanta Software Limited - * All rights reserved - */ - -#ifndef CS_FW_SRC_MGOS_UPDATER_UTIL_H_ -#define CS_FW_SRC_MGOS_UPDATER_UTIL_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* Function to merge filesystems. */ -bool mgos_upd_merge_fs(const char *old_fs_path, const char *new_fs_path); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CS_FW_SRC_MGOS_UPDATER_UTIL_H_ */ diff --git a/libs/ota-common/mos.yml b/libs/ota-common/mos.yml deleted file mode 100644 index bd198dd..0000000 --- a/libs/ota-common/mos.yml +++ /dev/null @@ -1,34 +0,0 @@ -author: mongoose-os -description: OTA common bits -type: lib -version: 1.0 - -platforms: [ cc3200, esp32, esp8266, stm32 ] - -sources: - - src - - src/${platform} - -includes: - - include - - include/${platform} - -libs: - - origin: https://github.com/mongoose-os-libs/mongoose - -no_implicit_init_deps: true -init_deps: - - mongoose - -config_schema: - - ["update", "o", {title: "Firmware updater"}] - - ["update.timeout", "i", 600, {title : "Update will be aborted if it does not finish within this time"}] - - ["update.commit_timeout", "i", {title : "After applying update, wait for commit up to this long"}] - -tags: - - c - - core - - ota - - docs:net:OTA - -manifest_version: 2018-06-20 diff --git a/libs/ota-common/mos_stm32.yml b/libs/ota-common/mos_stm32.yml deleted file mode 100644 index e8d593e..0000000 --- a/libs/ota-common/mos_stm32.yml +++ /dev/null @@ -1,2 +0,0 @@ -libs: - - origin: https://github.com/mongoose-os-libs/bootloader diff --git a/libs/ota-common/src/cc3200/cc3200_updater.c b/libs/ota-common/src/cc3200/cc3200_updater.c deleted file mode 100644 index fbdad99..0000000 --- a/libs/ota-common/src/cc3200/cc3200_updater.c +++ /dev/null @@ -1,505 +0,0 @@ -/* - * Copyright (c) 2014-2018 Cesanta Software Limited - * All rights reserved - * - * Licensed under the Apache License, Version 2.0 (the ""License""); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an ""AS IS"" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "cc3200_updater.h" - -#include -#include - -#include "common/cs_dbg.h" -#include "common/platform.h" - -#include "frozen.h" -#include "mongoose.h" - -#include "mgos_hal.h" -#include "mgos_sys_config.h" -#include "mgos_updater_hal.h" -#include "mgos_updater_util.h" -#include "mgos_utils.h" - -#include "cc32xx_hash.h" - -#include "cc3200_vfs_dev_slfs_container.h" -#include "cc3200_vfs_dev_slfs_container_meta.h" -#include "fw/platforms/cc3200/boot/lib/boot.h" - -static int s_boot_cfg_idx; -static struct boot_cfg s_boot_cfg; - -struct mgos_upd_hal_ctx { - struct json_token *parts; - int cur_boot_cfg_idx; - int new_boot_cfg_idx; - char app_image_file[MAX_APP_IMAGE_FILE_LEN]; - uint32_t app_load_addr; - char fs_container_file[MAX_FS_CONTAINER_FNAME_LEN + 3]; - uint32_t fs_size, fs_block_size, fs_page_size, fs_erase_size; - struct json_token cur_part; - const _u8 *cur_fn; - _i32 cur_fh; - const char *status_msg; -}; - -struct mgos_upd_hal_ctx *mgos_upd_hal_ctx_create(void) { - struct mgos_upd_hal_ctx *ctx = - (struct mgos_upd_hal_ctx *) calloc(1, sizeof(*ctx)); - ctx->cur_fh = -1; - return ctx; -} - -const char *mgos_upd_get_status_msg(struct mgos_upd_hal_ctx *ctx) { - return ctx->status_msg; -} - -int mgos_upd_begin(struct mgos_upd_hal_ctx *ctx, struct json_token *parts) { - ctx->parts = parts; - /* We want to make sure device uses boto loader. */ - ctx->cur_boot_cfg_idx = get_active_boot_cfg_idx(); - if (ctx->cur_boot_cfg_idx < 0) { - ctx->status_msg = "Could not read current boot cfg"; - return -1; - } - ctx->new_boot_cfg_idx = get_inactive_boot_cfg_idx(); - return 1; -} - -typedef int (*read_file_cb_t)(_u8 *data, int len, void *arg); -static int read_file(const char *fn, int offset, int len, read_file_cb_t cb, - void *arg) { - _i32 fh; - int r = sl_FsOpen((const _u8 *) fn, FS_MODE_OPEN_READ, NULL, &fh); - if (r < 0) return r; - while (len > 0) { - _u8 buf[512]; - int to_read = MIN(len, sizeof(buf)); - r = sl_FsRead(fh, offset, buf, to_read); - if (r != to_read) break; - if (cb(buf, to_read, arg) != to_read) break; - offset += to_read; - len -= to_read; - } - sl_FsClose(fh, NULL, NULL, 0); - return (len == 0 ? 0 : -1); -} - -static int sha1_update_cb(_u8 *data, int len, void *arg) { - cc32xx_hash_update((struct cc32xx_hash_ctx *) arg, data, len); - return len; -} - -void bin2hex(const uint8_t *src, int src_len, char *dst); - -static bool verify_checksum(const char *fn, int fs, - const struct json_token *expected) { - _u8 digest[20]; - char digest_str[41]; - - if (expected->len != 40) { - return false; - } - - struct cc32xx_hash_ctx ctx; - cc32xx_hash_init(&ctx); - cc32xx_hash_start(&ctx, CC32XX_HASH_ALGO_SHA1); - if (read_file(fn, 0, fs, sha1_update_cb, &ctx) < 0) { - return false; - } - cc32xx_hash_finish(&ctx, digest); - bin2hex(digest, 20, digest_str); - - LOG(LL_INFO, - ("%s: have %.*s, want %.*s", fn, 40, digest_str, 40, expected->ptr)); - - return (strncasecmp(expected->ptr, digest_str, 40) == 0); -} - -/* Create file name by appending ".$idx" to prefix. */ -static void create_fname(struct mg_str pfx, int idx, char *fn, int len) { - int l = MIN(len - 3, pfx.len); - memcpy(fn, pfx.p, l); - fn[l++] = '.'; - fn[l++] = ('0' + idx); - fn[l] = '\0'; -} - -static int prepare_to_write(struct mgos_upd_hal_ctx *ctx, - const struct mgos_upd_file_info *fi, - const char *fname, uint32_t falloc, - struct json_token *part) { - struct json_token expected_sha1 = JSON_INVALID_TOKEN; - json_scanf(part->ptr, part->len, "{cs_sha1: %T}", &expected_sha1); - if (verify_checksum(fname, fi->size, &expected_sha1)) { - LOG(LL_INFO, - ("Digest matched for %s %u (%.*s)", fname, (unsigned int) fi->size, - (int) expected_sha1.len, expected_sha1.ptr)); - return 0; - } - LOG(LL_INFO, ("Storing %s %u -> %s %u (%.*s)", fi->name, - (unsigned int) fi->size, fname, (unsigned int) falloc, - (int) expected_sha1.len, expected_sha1.ptr)); - ctx->cur_fn = (const _u8 *) fname; - sl_FsDel(ctx->cur_fn, 0); - _i32 r = sl_FsOpen(ctx->cur_fn, FS_MODE_OPEN_CREATE(falloc, 0), NULL, - &ctx->cur_fh); - if (r < 0) { - ctx->status_msg = "Failed to create file"; - return r; - } - return 1; -} - -struct find_part_info { - const char *src; - struct mg_str *key; - struct json_token *value; - char buf[50]; -}; - -static void find_part(void *data, const char *name, size_t name_len, - const char *path, const struct json_token *tok) { - struct find_part_info *info = (struct find_part_info *) data; - size_t path_len = strlen(path), src_len = strlen(info->src); - - (void) name; - (void) name_len; - - if (tok->ptr == NULL) { - /* - * We're not interested here in the events for which we have no value; - * namely, JSON_TYPE_OBJECT_START and JSON_TYPE_ARRAY_START - */ - return; - } - - /* For matched 'src' attribute, remember parent object path. */ - if (src_len == tok->len && strncmp(info->src, tok->ptr, tok->len) == 0) { - const char *p = path + path_len; - while (--p > path + 1) { - if (*p == '.') break; - } - - info->key->len = snprintf(info->buf, sizeof(info->buf), "%.*s", - (p - path) - 1, path + 1); - info->key->p = info->buf; - } - - /* - * And store parent's object token. These conditionals are triggered - * in separate callback invocations. - */ - if (info->value->len == 0 && info->key->len > 0 && - info->key->len + 1 == path_len && - strncmp(path + 1, info->key->p, info->key->len) == 0) { - *info->value = *tok; - } -} - -static int tcmp(const struct json_token *tok, const char *str) { - struct mg_str s = {.p = tok->ptr, .len = tok->len}; - return mg_vcmp(&s, str); -} - -enum mgos_upd_file_action mgos_upd_file_begin( - struct mgos_upd_hal_ctx *ctx, const struct mgos_upd_file_info *fi) { - struct mg_str part_name = MG_MK_STR(""); - enum mgos_upd_file_action ret = MGOS_UPDATER_SKIP_FILE; - struct find_part_info find_part_info = {fi->name, &part_name, &ctx->cur_part}; - ctx->cur_part.len = part_name.len = 0; - json_walk(ctx->parts->ptr, ctx->parts->len, find_part, &find_part_info); - if (ctx->cur_part.len == 0) return ret; - /* Drop any indexes from part name, we'll add our own. */ - while (1) { - char c = part_name.p[part_name.len - 1]; - if (c != '.' && !(c >= '0' && c <= '9')) break; - part_name.len--; - } - struct json_token type = JSON_INVALID_TOKEN; - const char *fname = NULL; - uint32_t falloc = 0; - json_scanf(ctx->cur_part.ptr, ctx->cur_part.len, - "{load_addr:%u, falloc:%u, type: %T}", &ctx->app_load_addr, - &falloc, &type); - - if (falloc == 0) falloc = fi->size; - if (tcmp(&type, "app") == 0) { - struct boot_cfg cur_cfg; - int r = read_boot_cfg(ctx->cur_boot_cfg_idx, &cur_cfg); - if (r < 0) { - ctx->status_msg = "Could not read current boot cfg"; - return MGOS_UPDATER_ABORT; - } -#if CC3200_SAFE_CODE_UPDATE - /* - * When safe code update is enabled, we write code to a new file. - * Otherwise we write to the same slot we're using currently, which is - * unsafe, makes reverting code update not possible, but saves space. - */ - create_fname( - mg_mk_str_n(cur_cfg.app_image_file, strlen(cur_cfg.app_image_file) - 2), - ctx->new_boot_cfg_idx, ctx->app_image_file, - sizeof(ctx->app_image_file)); -#else - { - strncpy(ctx->app_image_file, cur_cfg.app_image_file, - sizeof(ctx->app_image_file)); - } -#endif - if (ctx->app_load_addr >= 0x20000000) { - fname = ctx->app_image_file; - } else { - ctx->status_msg = "Bad/missing app load_addr"; - ret = MGOS_UPDATER_ABORT; - } - } else if (tcmp(&type, "fs") == 0) { - json_scanf( - ctx->cur_part.ptr, ctx->cur_part.len, - "{fs_size: %u, fs_block_size: %u, fs_page_size: %u, fs_erase_size: %u}", - &ctx->fs_size, &ctx->fs_block_size, &ctx->fs_page_size, - &ctx->fs_erase_size); - if (ctx->fs_size > 0 && ctx->fs_block_size > 0 && ctx->fs_page_size > 0 && - ctx->fs_erase_size > 0) { - char fs_container_prefix[MAX_FS_CONTAINER_PREFIX_LEN]; - create_fname(part_name, ctx->new_boot_cfg_idx, fs_container_prefix, - sizeof(fs_container_prefix)); - /* Delete container 1 (if any) so that 0 is the only one. */ - cc3200_vfs_dev_slfs_container_delete_container(fs_container_prefix, 1); - cc3200_vfs_dev_slfs_container_fname(fs_container_prefix, 0, - (_u8 *) ctx->fs_container_file); - fname = ctx->fs_container_file; - if (fi->size > ctx->fs_size) { - /* Assume meta has already been added. */ - falloc = fi->size; - } else { - falloc = FS_CONTAINER_SIZE(fi->size); - } - } else { - ctx->status_msg = "Missing FS parameters"; - ret = MGOS_UPDATER_ABORT; - } - } - if (fname != NULL) { - int r = prepare_to_write(ctx, fi, fname, falloc, &ctx->cur_part); - if (r < 0) { - LOG(LL_ERROR, ("err = %d", r)); - ret = MGOS_UPDATER_ABORT; - } else { - ret = (r > 0 ? MGOS_UPDATER_PROCESS_FILE : MGOS_UPDATER_SKIP_FILE); - } - } - if (ret == MGOS_UPDATER_SKIP_FILE) { - DBG(("Skipping %s %.*s", fi->name, (int) part_name.len, part_name.p)); - } - return ret; -} - -int mgos_upd_file_data(struct mgos_upd_hal_ctx *ctx, - const struct mgos_upd_file_info *fi, - struct mg_str data) { - _i32 r = sl_FsWrite(ctx->cur_fh, fi->processed, (_u8 *) data.p, data.len); - if (r != data.len) { - ctx->status_msg = "Write failed"; - r = -1; - } - return r; -} - -int mgos_upd_file_end(struct mgos_upd_hal_ctx *ctx, - const struct mgos_upd_file_info *fi, struct mg_str tail) { - int r = tail.len; - assert(tail.len == 0); - if (ctx->cur_fn == (_u8 *) ctx->fs_container_file) { - if (!cc3200_vfs_dev_slfs_container_write_meta( - ctx->cur_fh, FS_INITIAL_SEQ, ctx->fs_size, ctx->fs_block_size, - ctx->fs_page_size, ctx->fs_erase_size)) { - ctx->status_msg = "Failed to write fs meta"; - r = -1; - } - } - if (sl_FsClose(ctx->cur_fh, NULL, NULL, 0) != 0) { - ctx->status_msg = "Close failed"; - r = -1; - } else { - struct json_token sha1 = JSON_INVALID_TOKEN; - json_scanf(ctx->cur_part.ptr, ctx->cur_part.len, "{cs_sha1: %T}", &sha1); - if (!verify_checksum((const char *) ctx->cur_fn, fi->size, &sha1)) { - ctx->status_msg = "Checksum mismatch"; - r = -1; - } - } - ctx->cur_fh = -1; - ctx->cur_fn = NULL; - return r; -} - -int mgos_upd_finalize(struct mgos_upd_hal_ctx *ctx) { - struct boot_cfg cur_cfg, new_cfg; - int r = read_boot_cfg(ctx->cur_boot_cfg_idx, &cur_cfg); - if (r < 0) { - ctx->status_msg = "Could not read current boot cfg"; - return r; - } - LOG(LL_INFO, - ("Boot cfg %d: 0x%llx, 0x%x, %s @ 0x%08x, %s", ctx->cur_boot_cfg_idx, - cur_cfg.seq, (unsigned int) cur_cfg.flags, cur_cfg.app_image_file, - (unsigned int) cur_cfg.app_load_addr, cur_cfg.fs_container_prefix)); - memset(&new_cfg, 0, sizeof(new_cfg)); - new_cfg.seq = cur_cfg.seq - 1; - new_cfg.flags |= BOOT_F_FIRST_BOOT; - if (ctx->app_image_file[0] != '\0') { - strncpy(new_cfg.app_image_file, ctx->app_image_file, - sizeof(new_cfg.app_image_file)); - new_cfg.app_load_addr = ctx->app_load_addr; - } else { - strcpy(new_cfg.app_image_file, cur_cfg.app_image_file); - new_cfg.app_load_addr = cur_cfg.app_load_addr; - } - if (ctx->fs_container_file[0] != '\0') { - int n = strlen(ctx->fs_container_file); - do { - n--; - } while (ctx->fs_container_file[n] != '.'); - strncpy(new_cfg.fs_container_prefix, ctx->fs_container_file, n); - new_cfg.flags |= BOOT_F_MERGE_SPIFFS; - } else { - strcpy(new_cfg.fs_container_prefix, cur_cfg.fs_container_prefix); - } - LOG(LL_INFO, - ("Boot cfg %d: 0x%llx, 0x%x, %s @ 0x%08x, %s", ctx->new_boot_cfg_idx, - new_cfg.seq, (unsigned int) new_cfg.flags, new_cfg.app_image_file, - (unsigned int) new_cfg.app_load_addr, new_cfg.fs_container_prefix)); - r = write_boot_cfg(&new_cfg, ctx->new_boot_cfg_idx); - if (r < 0) { - ctx->status_msg = "Could not write new boot cfg"; - return r; - } - return 1; -} - -void mgos_upd_hal_ctx_free(struct mgos_upd_hal_ctx *ctx) { - if (ctx == NULL) return; - if (ctx->cur_fh >= 0) sl_FsClose(ctx->cur_fh, NULL, NULL, 0); - if (ctx->cur_fn != NULL) sl_FsDel(ctx->cur_fn, 0); - memset(ctx, 0, sizeof(*ctx)); - free(ctx); -} - -int mgos_upd_create_snapshot() { - /* TODO(rojer): Implement. */ - return -1; -} - -bool cc3200_upd_init(void) { - s_boot_cfg_idx = get_active_boot_cfg_idx(); - if (s_boot_cfg_idx < 0 || read_boot_cfg(s_boot_cfg_idx, &s_boot_cfg) < 0) { - return false; - } - - LOG(LL_INFO, - ("Boot cfg %d: 0x%llx, 0x%u, %s @ 0x%08x, %s", s_boot_cfg_idx, - s_boot_cfg.seq, (unsigned int) s_boot_cfg.flags, - s_boot_cfg.app_image_file, (unsigned int) s_boot_cfg.app_load_addr, - s_boot_cfg.fs_container_prefix)); - - if (mgos_upd_is_first_boot()) { - /* Tombstone the current config. If anything goes wrong between now and - * commit, next boot will use the old one. */ - uint64_t saved_seq = s_boot_cfg.seq; - s_boot_cfg.seq = BOOT_CFG_TOMBSTONE_SEQ; - write_boot_cfg(&s_boot_cfg, s_boot_cfg_idx); - s_boot_cfg.seq = saved_seq; - } - - /* - * We aim to maintain at most 3 FS containers at all times. - * Delete inactive FS container in the inactive boot configuration. - */ - struct boot_cfg cfg; - int inactive_idx = (s_boot_cfg_idx == 0 ? 1 : 0); - if (read_boot_cfg(inactive_idx, &cfg) >= 0) { - cc3200_vfs_dev_slfs_container_delete_inactive_container( - cfg.fs_container_prefix); - } - - return true; -} - -const char *cc3200_upd_get_fs_container_prefix(void) { - return s_boot_cfg.fs_container_prefix; -} - -bool mgos_upd_boot_get_state(struct mgos_upd_boot_state *bs) { - struct boot_cfg *cfg = &s_boot_cfg; - memset(bs, 0, sizeof(*bs)); - const char *p = strrchr(cfg->app_image_file, '.'); - bs->active_slot = (*(p + 1) == '0' ? 0 : 1); - bs->revert_slot = (bs->active_slot == 0 ? 1 : 0); - bs->is_committed = !(cfg->flags & BOOT_F_FIRST_BOOT); - return true; -} - -bool mgos_upd_boot_set_state(const struct mgos_upd_boot_state *bs) { - /* TODO(rojer): Implement. */ - (void) bs; - return false; -} - -void mgos_upd_boot_revert(void) { - int boot_cfg_idx = s_boot_cfg_idx; - struct boot_cfg *cfg = &s_boot_cfg; - if (!cfg->flags & BOOT_F_FIRST_BOOT) return; - LOG(LL_ERROR, ("Config %d is bad, reverting", boot_cfg_idx)); - /* Tombstone the current config. */ - cfg->seq = BOOT_CFG_TOMBSTONE_SEQ; - write_boot_cfg(cfg, boot_cfg_idx); -} - -bool mgos_upd_is_first_boot(void) { - return (s_boot_cfg.flags & BOOT_F_FIRST_BOOT) != 0; -} - -void mgos_upd_boot_commit(void) { - int boot_cfg_idx = s_boot_cfg_idx; - struct boot_cfg *cfg = &s_boot_cfg; - if (!cfg->flags & BOOT_F_FIRST_BOOT) return; - cfg->flags &= ~(BOOT_F_FIRST_BOOT); - int r = write_boot_cfg(cfg, boot_cfg_idx); - if (r < 0) mgos_upd_boot_revert(); - LOG(LL_INFO, ("Committed cfg %d, seq 0x%llx", boot_cfg_idx, cfg->seq)); -} - -int mgos_upd_apply_update(void) { - int boot_cfg_idx = s_boot_cfg_idx; - struct boot_cfg *cfg = &s_boot_cfg; - if (cfg->flags & BOOT_F_MERGE_SPIFFS) { - int old_boot_cfg_idx = (boot_cfg_idx == 0 ? 1 : 0); - struct boot_cfg old_boot_cfg; - int r = read_boot_cfg(old_boot_cfg_idx, &old_boot_cfg); - if (r < 0) return r; - if (!cc3200_fs_container_mount("/old", old_boot_cfg.fs_container_prefix)) { - return -123; - } - if (mgos_upd_merge_fs("/old", "/")) { - r = 0; - cfg->flags &= ~(BOOT_F_MERGE_SPIFFS); - } else { - r = -124; - } - mgos_vfs_umount("/old"); - } - return 0; -} diff --git a/libs/ota-common/src/esp32/esp32_updater.c b/libs/ota-common/src/esp32/esp32_updater.c deleted file mode 100644 index 8e2e390..0000000 --- a/libs/ota-common/src/esp32/esp32_updater.c +++ /dev/null @@ -1,645 +0,0 @@ -/* - * Copyright (c) 2014-2018 Cesanta Software Limited - * All rights reserved - * - * Licensed under the Apache License, Version 2.0 (the ""License""); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an ""AS IS"" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "esp32_updater.h" - -#include -#include - -#include "esp_flash_encrypt.h" -#include "esp_flash_partitions.h" -#include "esp_ota_ops.h" -#include "esp_partition.h" -#include "esp_spi_flash.h" -#include "nvs.h" - -#include "mbedtls/sha1.h" - -#include "common/cs_dbg.h" -#include "common/mg_str.h" -#include "common/platform.h" - -#include "frozen.h" -#include "mongoose.h" - -#include "mgos_hal.h" -#include "mgos_sys_config.h" -#include "mgos_updater_hal.h" -#include "mgos_updater_util.h" -#include "mgos_utils.h" -#include "mgos_vfs.h" -#include "mgos_vfs_internal.h" - -#include "esp32_fs.h" - -/* - * Since boot loader does not provide a way to store flags, we use NVS. - * We store a 32-bit uint with old slot, new slot and first boot flag. - */ -#define MGOS_UPDATE_NVS_NAMESPACE "mgos" -#define MGOS_UPDATE_NVS_KEY_FLAGS "update" -#define MGOS_UPDATE_MERGE_FS 0x200 -#define MGOS_UPDATE_FIRST_BOOT 0x100 -#define MGOS_UPDATE_NVS_VAL(old_slot, new_slot, first_boot, merge_fs) \ - (((merge_fs) ? MGOS_UPDATE_MERGE_FS : 0) | \ - ((first_boot) ? MGOS_UPDATE_FIRST_BOOT : 0) | (((new_slot) &0xf) << 4) | \ - ((old_slot) &0xf)) -#define MGOS_UPDATE_OLD_SLOT(v) ((v) &0x0f) -#define MGOS_UPDATE_NEW_SLOT(v) (((v) >> 4) & 0x0f) -/* In this key we store type and opts of the old FS. */ -#define MGOS_UPDATE_NVS_KEY_OLD_FS "update_old_fs" - -#define CS_LEN 20 /* SHA1 */ -#define CS_HEX_LEN (CS_LEN * 2) -#define CS_HEX_BUF_SIZE (CS_HEX_LEN + 1) - -#define FLASH_PARAMS_ADDR 0x1000 -#define FLASH_PARAMS_LEN 4 -#define LABEL_OFFSET 8 - -#if (FLASH_PARAMS_ADDR % MGOS_UPDATER_DATA_CHUNK_SIZE != 0) || \ - MGOS_UPDATER_DATA_CHUNK_SIZE < FLASH_PARAMS_LEN -#error "Don't do that" -#endif - -uint32_t g_boot_status = 0; - -struct mgos_upd_hal_ctx { - const char *status_msg; - - struct json_token boot_file_name, boot_cs_sha1; - uint32_t boot_addr; - bool update_bootloader; - /* - * Note: Latter 8 bytes of the label are used to store flash params: - * 4 bytes are the current device's flash params, - * 4 bytes are the ones in the image. We substitute device's params - * during writing but use bytes fro flash image when computing the checksum. - */ - esp_partition_t boot_partition; - - const esp_partition_t *cur_app_partition; - - struct json_token app_file_name, app_cs_sha1; - const esp_partition_t *app_partition; - esp_ota_handle_t app_ota_handle; - - struct json_token fs_file_name, fs_cs_sha1; - const esp_partition_t *fs_partition; - - size_t write_offset; - struct json_token *cs_sha1; - const esp_partition_t *write_partition; -}; - -struct mgos_upd_hal_ctx *mgos_upd_hal_ctx_create(void) { - struct mgos_upd_hal_ctx *ctx = - (struct mgos_upd_hal_ctx *) calloc(1, sizeof(*ctx)); - return ctx; -} - -const char *mgos_upd_get_status_msg(struct mgos_upd_hal_ctx *ctx) { - return ctx->status_msg; -} - -static int find_inactive_slot(const esp_partition_t **cur_app_partition, - const esp_partition_t **cur_fs_partition, - const esp_partition_t **new_app_partition, - const esp_partition_t **new_fs_partition, - const char **status_msg) { - *cur_app_partition = esp_ota_get_boot_partition(); - if (*cur_app_partition == NULL) { - *status_msg = "Not in OTA boot mode"; - return -1; - } - int slot = SUBTYPE_TO_SLOT((*cur_app_partition)->subtype); - *cur_fs_partition = esp32_find_fs_for_app_slot(slot); - /* Find next OTA slot */ - do { - slot = (slot + 1) & (NUM_OTA_SLOTS - 1); - esp_partition_subtype_t subtype = ESP_PARTITION_SUBTYPE_OTA(slot); - if (subtype == (*cur_app_partition)->subtype) break; - *new_app_partition = - esp_partition_find_first(ESP_PARTITION_TYPE_APP, subtype, NULL); - } while (*new_app_partition == NULL); - if (*new_app_partition == NULL) { - *status_msg = "No app slots"; - return -2; - } - *new_fs_partition = esp32_find_fs_for_app_slot(slot); - if (*new_fs_partition == NULL) { - *status_msg = "No fs slots"; - return -3; - } - return slot; -} - -int mgos_upd_begin(struct mgos_upd_hal_ctx *ctx, struct json_token *parts) { - const esp_partition_t *cur_fs_partition; - int slot = find_inactive_slot(&ctx->cur_app_partition, &cur_fs_partition, - &ctx->app_partition, &ctx->fs_partition, - &ctx->status_msg); - if (slot < 0) { - return slot; - } - - uint32_t boot_addr = 0; - int update_bootloader = false; - json_scanf(parts->ptr, parts->len, - "{boot: {src: %T, addr: %u, cs_sha1: %T, update: %B}, " - "app: {src: %T, cs_sha1: %T}, " - "fs: {src: %T, cs_sha1: %T}}", - &ctx->boot_file_name, &boot_addr, &ctx->boot_cs_sha1, - &update_bootloader, &ctx->app_file_name, &ctx->app_cs_sha1, - &ctx->fs_file_name, &ctx->fs_cs_sha1); - - if (ctx->app_file_name.len == 0 || ctx->app_cs_sha1.len == 0 || - ctx->fs_file_name.len == 0 || ctx->fs_cs_sha1.len == 0 || - (ctx->update_bootloader && - (ctx->boot_file_name.len == 0 || ctx->boot_cs_sha1.len == 0))) { - ctx->status_msg = "Incomplete update package"; - return -3; - } - - ctx->boot_addr = boot_addr; - ctx->update_bootloader = update_bootloader; - if (ctx->update_bootloader) { - /* Create a bootloader "partition", so esp_partition_* API can be used. */ - ctx->boot_partition.address = 0x1000; - ctx->boot_partition.size = (CONFIG_PARTITION_TABLE_OFFSET - 0x1000); - /* If encryption is enabled, boot loader must be encrypted. */ - ctx->boot_partition.encrypted = esp_flash_encryption_enabled(); - if (boot_addr > ctx->boot_partition.size) { - ctx->status_msg = "Invalid boot write addr"; - return -4; - } - strcpy(ctx->boot_partition.label, "boot"); - if (esp_partition_read(&ctx->boot_partition, FLASH_PARAMS_ADDR, - ctx->boot_partition.label + LABEL_OFFSET, - FLASH_PARAMS_LEN) != 0) { - ctx->status_msg = "Failed to read flash params"; - return -5; - } - LOG(LL_INFO, ("Boot: %.*s -> 0x%x, current flash params: 0x%02x%02x", - (int) ctx->boot_file_name.len, ctx->boot_file_name.ptr, - ctx->boot_addr, ctx->boot_partition.label[LABEL_OFFSET + 2], - ctx->boot_partition.label[LABEL_OFFSET + 3])); - } - - LOG(LL_INFO, ("App: %.*s -> %s, FS: %.*s -> %s", (int) ctx->app_file_name.len, - ctx->app_file_name.ptr, ctx->app_partition->label, - (int) ctx->fs_file_name.len, ctx->fs_file_name.ptr, - ctx->fs_partition->label)); - - return 1; -} - -void bin2hex(const uint8_t *src, int src_len, char *dst); - -static bool compute_checksum(const esp_partition_t *p, size_t len, - char *cs_hex) { - bool ret = false; - size_t offset = 0; - mbedtls_sha1_context sha1_ctx; - unsigned char digest[CS_LEN]; - mbedtls_sha1_init(&sha1_ctx); - mbedtls_sha1_starts(&sha1_ctx); - while (offset < len) { - uint8_t tmp[MGOS_UPDATER_DATA_CHUNK_SIZE]; - size_t block_len = len - offset; - if (block_len > sizeof(tmp)) block_len = sizeof(tmp); - esp_err_t err = esp_partition_read(p, offset, tmp, block_len); - if (err != ESP_OK) { - LOG(LL_ERROR, - ("Error reading %s at offset %u: %d", p->label, offset, err)); - goto cleanup; - } - /* Special handling of flash params. It's a bit simpler than when writing - * because we know that FLASH_PARAMS_ADDR divides WRITE_CHUNK_SIZE and - * WRITE_CHUNK_SIZE is > FLASH_PARAMS_LEN. */ - if (p->address + offset == FLASH_PARAMS_ADDR) { - LOG(LL_DEBUG, - ("Swapping %d @ 0x%x", FLASH_PARAMS_LEN, p->address + offset)); - memcpy(tmp, p->label + LABEL_OFFSET + FLASH_PARAMS_LEN, FLASH_PARAMS_LEN); - } - mbedtls_sha1_update(&sha1_ctx, tmp, block_len); - offset += block_len; - } - mbedtls_sha1_finish(&sha1_ctx, digest); - bin2hex(digest, CS_LEN, cs_hex); - cs_hex[CS_HEX_LEN] = '\0'; - ret = true; - -cleanup: - mbedtls_sha1_free(&sha1_ctx); - return ret; -} - -static bool verify_checksum(const esp_partition_t *p, size_t len, - const char *exp_sha1, bool critical) { - char cs_hex[CS_HEX_BUF_SIZE]; - bool ret = compute_checksum(p, len, cs_hex) && - (strncmp(cs_hex, exp_sha1, CS_HEX_LEN) == 0); - LOG((ret || !critical ? LL_DEBUG : LL_ERROR), - ("%s: %u @ 0x%x, cs_sha1 %s, expected %.*s", p->label, len, p->address, - cs_hex, CS_HEX_LEN, exp_sha1)); - return ret; -} - -enum mgos_upd_file_action mgos_upd_file_begin( - struct mgos_upd_hal_ctx *ctx, const struct mgos_upd_file_info *fi) { - esp_err_t err; - ctx->write_offset = 0; - ctx->write_partition = NULL; - if (strncmp(fi->name, ctx->app_file_name.ptr, ctx->app_file_name.len) == 0) { - if (verify_checksum(ctx->app_partition, fi->size, ctx->app_cs_sha1.ptr, - false /* critical */)) { - LOG(LL_INFO, ("Skip writing app (digest matches)")); - return MGOS_UPDATER_SKIP_FILE; - } - LOG(LL_INFO, ("Writing app image @ 0x%x", ctx->app_partition->address)); - if (esp_ota_begin(ctx->app_partition, 0, &ctx->app_ota_handle) != ESP_OK) { - ctx->status_msg = "Failed to start app write"; - return MGOS_UPDATER_ABORT; - } - ctx->cs_sha1 = &ctx->app_cs_sha1; - ctx->write_partition = ctx->app_partition; - return MGOS_UPDATER_PROCESS_FILE; - } - if (ctx->update_bootloader && - strncmp(fi->name, ctx->boot_file_name.ptr, ctx->boot_file_name.len) == - 0) { - LOG(LL_INFO, ("Writing boot loader @ 0x%x", ctx->boot_addr)); - ctx->write_partition = &ctx->boot_partition; - ctx->write_offset = ctx->boot_addr; - ctx->cs_sha1 = &ctx->boot_cs_sha1; - } else if (strncmp(fi->name, ctx->fs_file_name.ptr, ctx->fs_file_name.len) == - 0) { - if (verify_checksum(ctx->fs_partition, fi->size, ctx->app_cs_sha1.ptr, - false /* critical */)) { - LOG(LL_INFO, ("Skip writing FS (digest matches)")); - return MGOS_UPDATER_SKIP_FILE; - } - LOG(LL_INFO, ("Writing FS image @ 0x%x", ctx->fs_partition->address)); - ctx->write_partition = ctx->fs_partition; - ctx->write_offset = 0; - ctx->cs_sha1 = &ctx->fs_cs_sha1; - } - if (ctx->write_partition != NULL) { - err = esp_partition_erase_range( - ctx->write_partition, ctx->write_offset, - ctx->write_partition->size - ctx->write_offset); - if (err != ESP_OK) { - ctx->status_msg = "Failed to start write"; - LOG(LL_ERROR, ("%s: %d", ctx->status_msg, err)); - return MGOS_UPDATER_ABORT; - } - return MGOS_UPDATER_PROCESS_FILE; - } - LOG(LL_DEBUG, ("Not interesting: %s", fi->name)); - return MGOS_UPDATER_SKIP_FILE; -} - -static void swap_flash_params(struct mgos_upd_hal_ctx *ctx, struct mg_str data, - bool save) { - if (ctx->write_partition != &ctx->boot_partition) return; - int off_s = MAX(0, FLASH_PARAMS_ADDR - (int) ctx->write_offset); - int off_d = MAX(0, (int) ctx->write_offset - FLASH_PARAMS_ADDR); - if (off_s >= data.len || off_d >= FLASH_PARAMS_LEN) return; - int len = MIN(FLASH_PARAMS_LEN - off_d, data.len - off_s); - if (save) { - LOG(LL_DEBUG, - ("Swapping %d @ 0x%x %d", len, (ctx->write_offset + off_s), off_d)); - memcpy(ctx->boot_partition.label + LABEL_OFFSET + FLASH_PARAMS_LEN + off_d, - data.p + off_s, len); - memcpy((char *) data.p + off_s, - ctx->boot_partition.label + LABEL_OFFSET + off_d, len); - } else { - memcpy((char *) data.p + off_s, - ctx->boot_partition.label + LABEL_OFFSET + FLASH_PARAMS_LEN + off_d, - len); - } -} - -int mgos_upd_file_data(struct mgos_upd_hal_ctx *ctx, - const struct mgos_upd_file_info *fi, - struct mg_str data) { - esp_err_t err = ESP_FAIL; - int to_process = (int) data.len; - if (strncmp(fi->name, ctx->app_file_name.ptr, ctx->app_file_name.len) == 0) { - err = esp_ota_write(ctx->app_ota_handle, data.p, to_process); - } else { - swap_flash_params(ctx, data, true /* save */); - err = esp_partition_write(ctx->write_partition, ctx->write_offset, data.p, - to_process); - swap_flash_params(ctx, data, false /* save */); - } - if (err != ESP_OK) { - LOG(LL_ERROR, - ("Write %d @ %d failed: %d", (int) data.len, ctx->write_offset, err)); - ctx->status_msg = "Failed to write data"; - return -1; - } - ctx->write_offset += to_process; - return to_process; -} - -int mgos_upd_file_end(struct mgos_upd_hal_ctx *ctx, - const struct mgos_upd_file_info *fi, struct mg_str tail) { - int ret = -1; - if (tail.len > 0) { - char tmp[MGOS_UPDATER_DATA_CHUNK_SIZE]; - memset(tmp, 0xff, sizeof(tmp)); - memcpy(tmp, tail.p, tail.len); - ret = mgos_upd_file_data(ctx, fi, mg_mk_str_n(tmp, sizeof(tmp))); - if (ret != sizeof(tmp)) { - ctx->status_msg = "Failed to write tail"; - return -1; - } - } - if (strncmp(fi->name, ctx->app_file_name.ptr, ctx->app_file_name.len) == 0) { - esp_ota_handle_t oh = ctx->app_ota_handle; - ctx->app_ota_handle = 0; - if (esp_ota_end(oh) != ESP_OK) { - ctx->app_ota_handle = 0; - ctx->status_msg = "Failed to finalize app write"; - return -2; - } - } else if (ctx->write_partition == &ctx->boot_partition) { - ctx->boot_partition.address = ctx->boot_addr; - } - if (!verify_checksum(ctx->write_partition, fi->size, ctx->cs_sha1->ptr, - true /* critical */)) { - ctx->status_msg = "Digest mismatch"; - return -10; - } else { - LOG(LL_INFO, ("%s verified (%.*s)", ctx->write_partition->label, - (int) ctx->cs_sha1->len, ctx->cs_sha1->ptr)); - } - return tail.len; -} - -static bool set_update_status(int old_slot, int new_slot, bool first_boot, - bool merge_fs) { - bool ret = false; - nvs_handle h; - esp_err_t err = nvs_open(MGOS_UPDATE_NVS_NAMESPACE, NVS_READWRITE, &h); - if (err != ESP_OK) { - LOG(LL_ERROR, ("Failed to open NVS: %d", err)); - return false; - } - const uint32_t val = - MGOS_UPDATE_NVS_VAL(old_slot, new_slot, first_boot, merge_fs); - err = nvs_set_u32(h, MGOS_UPDATE_NVS_KEY_FLAGS, val); - if (err != ESP_OK) { - LOG(LL_ERROR, ("Failed to set: %d", err)); - goto cleanup; - } - LOG(LL_DEBUG, ("New status: %08x", val)); - if (first_boot && merge_fs) { - char *old_fs_val; - mg_asprintf(&old_fs_val, 0, "%s %s", mgos_vfs_get_root_fs_type(), - mgos_vfs_get_root_fs_opts()); - err = nvs_set_str(h, MGOS_UPDATE_NVS_KEY_OLD_FS, old_fs_val); - free(old_fs_val); - } - nvs_commit(h); - g_boot_status = val; - ret = true; - -cleanup: - nvs_close(h); - return ret; -} - -static uint32_t get_update_status() { - uint32_t val = 0; - nvs_handle h; - esp_err_t err = nvs_open(MGOS_UPDATE_NVS_NAMESPACE, NVS_READONLY, &h); - if (err != ESP_OK) { - /* This is normal, meaning no update has taken place yet. */ - if (err != ESP_ERR_NVS_NOT_FOUND) { - LOG(LL_ERROR, ("Failed to open NVS: %d", err)); - } - return val; - } - err = nvs_get_u32(h, MGOS_UPDATE_NVS_KEY_FLAGS, &val); - if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) { - LOG(LL_ERROR, ("Failed to get: %d", err)); - goto cleanup; - } - LOG(LL_DEBUG, ("Update status: %08x", val)); - -cleanup: - nvs_close(h); - return val; -} - -static bool esp32_set_boot_slot(int slot) { - const esp_partition_t *p = esp_partition_find_first( - ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_OTA(slot), NULL); - if (p == NULL) return false; - LOG(LL_INFO, ("Setting boot partition to %s", p->label)); - return (esp_ota_set_boot_partition(p) == ESP_OK); -} - -int mgos_upd_finalize(struct mgos_upd_hal_ctx *ctx) { - if (!set_update_status(SUBTYPE_TO_SLOT(ctx->cur_app_partition->subtype), - SUBTYPE_TO_SLOT(ctx->app_partition->subtype), - true /* first_boot */, true /* merge_fs */)) { - ctx->status_msg = "Failed to set update status"; - return -1; - } - if (!esp32_set_boot_slot(SUBTYPE_TO_SLOT(ctx->app_partition->subtype))) { - ctx->status_msg = "Failed to set boot partition"; - return -1; - } - return 1; -} - -void mgos_upd_hal_ctx_free(struct mgos_upd_hal_ctx *ctx) { - if (ctx == NULL) return; - if (ctx->app_ota_handle != 0) { - esp_ota_end(ctx->app_ota_handle); - } - memset(ctx, 0, sizeof(*ctx)); - free(ctx); -} - -static bool copy_partition(const esp_partition_t *src, - const esp_partition_t *dst) { - esp_err_t err; - if (src->size > dst->size) return false; - char cs_hex[CS_HEX_BUF_SIZE]; - if (!compute_checksum(src, src->size, cs_hex)) { - return false; - } - if (verify_checksum(dst, src->size, cs_hex, false /* critical */)) { - LOG(LL_INFO, - ("%s -> %s: digest matched (%s)", src->label, dst->label, cs_hex)); - return true; - } - if ((err = esp_partition_erase_range(dst, 0, src->size)) != ESP_OK) { - LOG(LL_ERROR, ("%s: erase %u failed: %d", dst->label, src->size, err)); - return false; - } - uint32_t offset = 0; - while (offset < src->size) { - uint32_t buf[128]; - uint32_t n = sizeof(buf); - if (n > src->size - offset) n = src->size - offset; - if ((err = esp_partition_read(src, offset, buf, n)) != ESP_OK) { - LOG(LL_ERROR, ("%s: read @ %u failed: %d", src->label, offset, err)); - return false; - } - if ((err = esp_partition_write(dst, offset, buf, n)) != ESP_OK) { - LOG(LL_ERROR, ("%s: write @ %u failed: %d", dst->label, offset, err)); - return false; - } - offset += n; - } - if (!verify_checksum(dst, src->size, cs_hex, true /* critical */)) { - return false; - } - LOG(LL_INFO, ("%s -> %s: copied %u bytes, SHA1 %s", src->label, dst->label, - offset, cs_hex)); - return true; -} - -int mgos_upd_create_snapshot() { - const esp_partition_t *cur_app_partition, *cur_fs_partition; - const esp_partition_t *new_app_partition, *new_fs_partition; - const char *status_msg = NULL; - int slot = - find_inactive_slot(&cur_app_partition, &cur_fs_partition, - &new_app_partition, &new_fs_partition, &status_msg); - if (slot < 0) { - LOG(LL_ERROR, ("%s", status_msg)); - return slot; - } - LOG(LL_INFO, ("Snapshot: %s -> %s, %s -> %s", cur_app_partition->label, - new_app_partition->label, cur_fs_partition->label, - new_fs_partition->label)); - if (!copy_partition(cur_app_partition, new_app_partition)) return -2; - if (!copy_partition(cur_fs_partition, new_fs_partition)) return -3; - LOG(LL_INFO, ("Snapshot created")); - return slot; -} - -bool mgos_upd_boot_get_state(struct mgos_upd_boot_state *bs) { - memset(bs, 0, sizeof(*bs)); - bs->active_slot = MGOS_UPDATE_NEW_SLOT(g_boot_status); - bs->revert_slot = MGOS_UPDATE_OLD_SLOT(g_boot_status); - bs->is_committed = (g_boot_status & MGOS_UPDATE_FIRST_BOOT) == 0; - return true; -} - -bool mgos_upd_boot_set_state(const struct mgos_upd_boot_state *bs) { - return (set_update_status(bs->revert_slot, bs->active_slot, - !bs->is_committed /* first_boot */, - false /* merge_fs */) && - esp32_set_boot_slot(bs->active_slot)); -} - -void mgos_upd_boot_revert(void) { - int slot = MGOS_UPDATE_OLD_SLOT(g_boot_status); - if (slot == MGOS_UPDATE_NEW_SLOT(g_boot_status)) return; - LOG(LL_ERROR, ("Reverting to slot %d", slot)); - set_update_status(slot, slot, false /* first_boot */, false /* merge_fs */); - esp32_set_boot_slot(slot); -} - -void mgos_upd_boot_commit(void) { - int slot = MGOS_UPDATE_NEW_SLOT(g_boot_status); - if (set_update_status(MGOS_UPDATE_OLD_SLOT(g_boot_status), slot, - false /* first_boot */, false /* merge_fs */) && - esp32_set_boot_slot(slot)) { - LOG(LL_INFO, ("Committed slot %d", slot)); - } else { - LOG(LL_ERROR, ("Failed to commit update")); - } -} - -int mgos_upd_apply_update(void) { - int ret = -1; - nvs_handle h = 0; - char *old_fs_val = NULL; - int old_slot = MGOS_UPDATE_OLD_SLOT(g_boot_status); - if (MGOS_UPDATE_NEW_SLOT(g_boot_status) == old_slot || - !(g_boot_status & MGOS_UPDATE_MERGE_FS)) { - return 0; - } - const esp_partition_t *old_fs_part = esp32_find_fs_for_app_slot(old_slot); - if (old_fs_part == NULL) { - LOG(LL_ERROR, ("No old FS partition")); - goto out; - } - - const char *old_fs_type = mgos_vfs_get_root_fs_type(); - const char *old_fs_opts = mgos_vfs_get_root_fs_opts(); - - esp_err_t err = nvs_open(MGOS_UPDATE_NVS_NAMESPACE, NVS_READONLY, &h); - if (err == ESP_OK) { - size_t l = 0; - if ((err = nvs_get_str(h, MGOS_UPDATE_NVS_KEY_OLD_FS, NULL, &l)) == - ESP_OK) { - old_fs_val = malloc(l); - if (old_fs_val != NULL && - (err = nvs_get_str(h, MGOS_UPDATE_NVS_KEY_OLD_FS, old_fs_val, &l)) == - ESP_OK) { - old_fs_type = old_fs_val; - char *sp = strchr(old_fs_val, ' '); - *sp = '\0'; - old_fs_opts = sp + 1; - } - } - } - - if (!esp32_fs_mount_part(old_fs_part->label, "/old", old_fs_type, - old_fs_opts)) { - LOG(LL_ERROR, ("Failed to mount old file system")); - return -1; - } - - ret = (mgos_upd_merge_fs("/old", "/") ? 0 : -2); - - mgos_vfs_umount("/old"); - -out: - free(old_fs_val); - if (h != 0) nvs_close(h); - return ret; -} - -void esp32_updater_early_init(void) { - g_boot_status = get_update_status(); - if (!mgos_upd_is_first_boot()) return; - /* - * Tombstone the current config. If anything goes wrong between now and - * commit, next boot will use the old slot. - */ - uint32_t bs = g_boot_status; - set_update_status(MGOS_UPDATE_OLD_SLOT(g_boot_status), - MGOS_UPDATE_OLD_SLOT(g_boot_status), false /* first_boot */, - false /* merge_fs */); - g_boot_status = bs; - esp32_set_boot_slot(MGOS_UPDATE_OLD_SLOT(g_boot_status)); -} - -bool mgos_upd_is_first_boot(void) { - return (g_boot_status & MGOS_UPDATE_FIRST_BOOT) != 0; -} diff --git a/libs/ota-common/src/esp8266/esp_updater.c b/libs/ota-common/src/esp8266/esp_updater.c deleted file mode 100644 index 2624381..0000000 --- a/libs/ota-common/src/esp8266/esp_updater.c +++ /dev/null @@ -1,481 +0,0 @@ -/* - * Copyright (c) 2014-2018 Cesanta Software Limited - * All rights reserved - * - * Licensed under the Apache License, Version 2.0 (the ""License""); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an ""AS IS"" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include -#include - -#include "common/cs_dbg.h" -#include "common/cs_sha1.h" -#include "common/platforms/esp8266/esp_missing_includes.h" -#include "common/platforms/esp8266/rboot/rboot/appcode/rboot-api.h" -#include "common/queue.h" - -#include "mgos_hal.h" -#include "mgos_sys_config.h" -#include "mgos_updater_hal.h" -#include "mgos_updater_util.h" -#include "mgos_vfs.h" - -#include "esp_flash_writer.h" -#include "esp_fs.h" -#include "esp_rboot.h" - -#define CS_LEN 20 /* SHA1 */ -#define CS_HEX_LEN (CS_LEN * 2) -#define CS_HEX_BUF_SIZE (CS_HEX_LEN + 1) - -#define BOOT_F_MERGE_FS (1U << 0) - -#define FW_SLOT_SIZE 0x100000 - -#define FLASH_PARAMS_ADDR 0 - -#define WRITE_CHUNK_SIZE 4 - -struct slot_info { - int id; - uint32_t fw_addr; - uint32_t fw_size; - uint32_t fw_slot_size; - uint32_t fs_addr; - uint32_t fs_size; - uint32_t fs_slot_size; -}; - -struct mgos_upd_hal_ctx { - const char *status_msg; - struct slot_info write_slot; - struct json_token boot_file_name, boot_cs_sha1; - struct json_token fw_file_name, fw_cs_sha1; - struct json_token fs_file_name, fs_cs_sha1; - uint32_t boot_addr, boot_size, fw_size, fs_size; - bool update_bootloader; - union { - uint8_t bytes[4]; - uint32_t align4; - } flash_params; - - struct esp_flash_write_ctx wctx; - const struct json_token *wcs; -}; - -static void get_slot_info(int id, struct slot_info *si) { - memset(si, 0, sizeof(*si)); - si->id = id; - if (id == 0) { - si->fw_addr = FW1_ADDR; - si->fs_addr = FW1_FS_ADDR; - } else { - si->fw_addr = FW2_ADDR; - si->fs_addr = FW2_FS_ADDR; - } - rboot_config *cfg = get_rboot_config(); - si->fw_size = cfg->roms_sizes[id]; - si->fw_slot_size = FW_SIZE; - si->fs_size = cfg->fs_sizes[id]; - si->fs_slot_size = FS_SIZE; -} - -struct mgos_upd_hal_ctx *mgos_upd_hal_ctx_create(void) { - return calloc(1, sizeof(struct mgos_upd_hal_ctx)); -} - -const char *mgos_upd_get_status_msg(struct mgos_upd_hal_ctx *ctx) { - return ctx->status_msg; -} - -int mgos_upd_begin(struct mgos_upd_hal_ctx *ctx, struct json_token *parts) { - struct json_token fs = JSON_INVALID_TOKEN, fw = JSON_INVALID_TOKEN; - if (json_scanf(parts->ptr, parts->len, "{fw: %T, fs: %T}", &fw, &fs) != 2) { - ctx->status_msg = "Invalid manifest"; - return -1; - } - uint32_t boot_addr = 0, fw_addr = 0, fs_addr = 0; - int update_bootloader = false; - json_scanf(parts->ptr, parts->len, - "{boot: {src: %T, addr: %u, cs_sha1: %T, update: %B}, " - "fw: {src: %T, addr: %u, cs_sha1: %T}, " - "fs: {src: %T, addr: %u, cs_sha1: %T}}", - &ctx->boot_file_name, &boot_addr, &ctx->boot_cs_sha1, - &update_bootloader, &ctx->fw_file_name, &fw_addr, &ctx->fw_cs_sha1, - &ctx->fs_file_name, &fs_addr, &ctx->fs_cs_sha1); - if (ctx->fw_file_name.len == 0 || ctx->fw_cs_sha1.len == 0 || - ctx->fs_file_name.len == 0 || ctx->fs_cs_sha1.len == 0 || fs_addr == 0 || - (ctx->update_bootloader && - (ctx->boot_file_name.len == 0 || ctx->boot_cs_sha1.len == 0))) { - ctx->status_msg = "Incomplete update package"; - return -3; - } - - if (ctx->fw_cs_sha1.len != CS_HEX_LEN || ctx->fs_cs_sha1.len != CS_HEX_LEN || - (ctx->update_bootloader && ctx->boot_cs_sha1.len != CS_HEX_LEN)) { - ctx->status_msg = "Invalid checksum format"; - return -4; - } - - struct mgos_upd_boot_state bs; - if (!mgos_upd_boot_get_state(&bs)) return -5; - int inactive_slot = (bs.active_slot == 0 ? 1 : 0); - get_slot_info(inactive_slot, &ctx->write_slot); - if (ctx->write_slot.fw_addr == 0) { - ctx->status_msg = "OTA is not supported in this build"; - return -5; - } - - ctx->boot_addr = boot_addr; - ctx->update_bootloader = update_bootloader; - if (ctx->update_bootloader) { - /* - * Preserve old flash params. - * We need bytes 2 and 3, but the first 2 bytes are constant anyway, so we - * read and write 4 for simplicity. - */ - if (spi_flash_read(FLASH_PARAMS_ADDR, &ctx->flash_params.align4, 4) != 0) { - ctx->status_msg = "Failed to read flash params"; - return -6; - } - LOG(LL_INFO, - ("Boot: %.*s -> 0x%x, current flash params: 0x%02x%02x", - (int) ctx->boot_file_name.len, ctx->boot_file_name.ptr, ctx->boot_addr, - ctx->flash_params.bytes[2], ctx->flash_params.bytes[3])); - } - - LOG(LL_INFO, - ("Slot %d, FW: %.*s -> 0x%x, FS %.*s -> 0x%x", ctx->write_slot.id, - (int) ctx->fw_file_name.len, ctx->fw_file_name.ptr, - ctx->write_slot.fw_addr, (int) ctx->fs_file_name.len, - ctx->fs_file_name.ptr, ctx->write_slot.fs_addr)); - - return 1; -} - -void bin2hex(const uint8_t *src, int src_len, char *dst); - -static bool compute_checksum(uint32_t addr, size_t len, char *cs_hex) { - cs_sha1_ctx ctx; - cs_sha1_init(&ctx); - while (len != 0) { - uint32_t read_buf[16]; - uint32_t to_read = sizeof(read_buf); - if (to_read > len) to_read = len; - if (spi_flash_read(addr, read_buf, to_read) != 0) { - LOG(LL_ERROR, ("Failed to read %d bytes from %X", to_read, addr)); - return false; - } - cs_sha1_update(&ctx, (uint8_t *) read_buf, to_read); - mgos_wdt_feed(); - addr += to_read; - len -= to_read; - } - uint8_t cs_buf[CS_LEN]; - cs_sha1_final(cs_buf, &ctx); - bin2hex(cs_buf, CS_LEN, cs_hex); - return true; -} - -static bool verify_checksum(uint32_t addr, size_t len, const char *exp_cs_hex, - bool critical) { - char cs_hex[CS_HEX_LEN + 1]; - if (!compute_checksum(addr, len, cs_hex)) return false; - bool ret = (strncasecmp(cs_hex, exp_cs_hex, CS_HEX_LEN) == 0); - LOG((ret || !critical ? LL_DEBUG : LL_ERROR), - ("SHA1 %u @ 0x%x = %.*s, want %.*s", len, addr, CS_HEX_LEN, cs_hex, - CS_HEX_LEN, exp_cs_hex)); - return ret; -} - -enum mgos_upd_file_action mgos_upd_file_begin( - struct mgos_upd_hal_ctx *ctx, const struct mgos_upd_file_info *fi) { - bool res = false; - struct esp_flash_write_ctx *wctx = &ctx->wctx; - if (ctx->update_bootloader && - strncmp(fi->name, ctx->boot_file_name.ptr, ctx->boot_file_name.len) == - 0) { - if (fi->size <= BOOT_CONFIG_ADDR) { - res = esp_init_flash_write_ctx(wctx, ctx->boot_addr, BOOT_CONFIG_ADDR); - ctx->wcs = &ctx->boot_cs_sha1; - ctx->boot_size = fi->size; - } else { - LOG(LL_ERROR, ("Boot loader too big.")); - res = false; - } - } else if (strncmp(fi->name, ctx->fw_file_name.ptr, ctx->fw_file_name.len) == - 0) { - res = esp_init_flash_write_ctx(wctx, ctx->write_slot.fw_addr, - ctx->write_slot.fw_slot_size); - ctx->wcs = &ctx->fw_cs_sha1; - ctx->fw_size = fi->size; - } else if (strncmp(fi->name, ctx->fs_file_name.ptr, ctx->fs_file_name.len) == - 0) { - res = esp_init_flash_write_ctx(wctx, ctx->write_slot.fs_addr, - ctx->write_slot.fs_slot_size); - ctx->wcs = &ctx->fs_cs_sha1; - ctx->fs_size = fi->size; - } else { - LOG(LL_DEBUG, ("Not interesting: %s", fi->name)); - return MGOS_UPDATER_SKIP_FILE; - } - if (!res) { - ctx->status_msg = "Failed to start write"; - return MGOS_UPDATER_ABORT; - } - if (fi->size > wctx->max_size) { - LOG(LL_ERROR, ("Cannot write %s (%u) @ 0x%x: max size %u", fi->name, - fi->size, wctx->addr, wctx->max_size)); - ctx->status_msg = "Image too big"; - return MGOS_UPDATER_ABORT; - } - wctx->max_size = fi->size; - if (verify_checksum(wctx->addr, fi->size, ctx->wcs->ptr, false)) { - LOG(LL_INFO, ("Skip writing %s (%u) @ 0x%x (digest matches)", fi->name, - fi->size, wctx->addr)); - return MGOS_UPDATER_SKIP_FILE; - } - LOG(LL_INFO, - ("Start writing %s (%u) @ 0x%x", fi->name, fi->size, wctx->addr)); - return MGOS_UPDATER_PROCESS_FILE; -} - -int mgos_upd_file_data(struct mgos_upd_hal_ctx *ctx, - const struct mgos_upd_file_info *fi, - struct mg_str data) { - int to_process = (data.len / WRITE_CHUNK_SIZE) * WRITE_CHUNK_SIZE; - if (to_process == 0) { - return 0; - } - - int num_written = esp_flash_write(&ctx->wctx, data); - if (num_written < 0) { - ctx->status_msg = "Write failed"; - } - (void) fi; - return num_written; -} - -int mgos_upd_file_end(struct mgos_upd_hal_ctx *ctx, - const struct mgos_upd_file_info *fi, struct mg_str tail) { - assert(tail.len < WRITE_CHUNK_SIZE); - if (tail.len > 0 && esp_flash_write(&ctx->wctx, tail) != (int) tail.len) { - ctx->status_msg = "Tail write failed"; - return -1; - } - if (!verify_checksum(ctx->wctx.addr, fi->size, ctx->wcs->ptr, true)) { - ctx->status_msg = "Invalid checksum"; - return -2; - } else { - LOG(LL_INFO, ("Write finished, checksum ok")); - } - if (ctx->update_bootloader && - strncmp(fi->name, ctx->boot_file_name.ptr, ctx->boot_file_name.len) == - 0) { - LOG(LL_INFO, ("Restoring flash params")); - if (spi_flash_write(FLASH_PARAMS_ADDR, &ctx->flash_params.align4, 4) != 0) { - ctx->status_msg = "Failed to write flash params"; - return -3; - } - } - memset(&ctx->wctx, 0, sizeof(ctx->wctx)); - return tail.len; -} - -int mgos_upd_finalize(struct mgos_upd_hal_ctx *ctx) { - if (ctx->fw_size == 0) { - ctx->status_msg = "Missing fw part"; - return -1; - } - if (ctx->fs_size == 0) { - ctx->status_msg = "Missing fs part"; - return -2; - } - - int slot = ctx->write_slot.id; - rboot_config *cfg = get_rboot_config(); - cfg->current_rom = slot; - cfg->previous_rom = (slot == 0 ? 1 : 0); - cfg->roms[slot] = ctx->write_slot.fw_addr; - cfg->roms_sizes[slot] = ctx->fw_size; - cfg->fs_addresses[slot] = ctx->write_slot.fs_addr; - cfg->fs_sizes[slot] = ctx->fs_size; - cfg->is_first_boot = cfg->fw_updated = true; - cfg->boot_attempts = 0; - cfg->user_flags |= BOOT_F_MERGE_FS; - if (!rboot_set_config(cfg)) { - ctx->status_msg = "Failed to set boot config"; - return -3; - } - - LOG(LL_INFO, - ("New rboot config: " - "prev_rom: %d, current_rom: %d current_rom addr: 0x%x, " - "current_rom size: %d, current_fs addr: 0x%0x, current_fs size: %d", - (int) cfg->previous_rom, (int) cfg->current_rom, - cfg->roms[cfg->current_rom], cfg->roms_sizes[cfg->current_rom], - cfg->fs_addresses[cfg->current_rom], cfg->fs_sizes[cfg->current_rom])); - - return 1; -} - -void mgos_upd_hal_ctx_free(struct mgos_upd_hal_ctx *ctx) { - memset(ctx, 0, sizeof(*ctx)); - free(ctx); -} - -int mgos_upd_apply_update(void) { - rboot_config *cfg = get_rboot_config(); - if (!cfg->user_flags & BOOT_F_MERGE_FS) return 0; - uint32_t old_fs_addr = cfg->fs_addresses[cfg->previous_rom]; - uint32_t old_fs_size = cfg->fs_sizes[cfg->previous_rom]; - LOG(LL_INFO, ("Mounting old FS: %d @ 0x%x", old_fs_size, old_fs_addr)); - if (!esp_fs_mount(old_fs_addr, old_fs_size, "oldroot", "/old")) { - LOG(LL_ERROR, ("Update failed: cannot mount previous file system")); - return -1; - } - - int ret = (mgos_upd_merge_fs("/old", "/") ? 0 : -2); - - mgos_vfs_umount("/old"); - mgos_vfs_dev_unregister("oldroot"); - - if (ret == 0) { - cfg->user_flags &= ~BOOT_F_MERGE_FS; - rboot_set_config(cfg); - } - - return ret; -} - -static bool copy_region(uint32_t src_addr, uint32_t dst_addr, size_t len) { - char cs_hex[CS_HEX_LEN + 1]; - if (!compute_checksum(src_addr, len, cs_hex)) return false; - if (verify_checksum(dst_addr, len, cs_hex, false)) { - LOG(LL_DEBUG, ("Skip copying %u @ 0x%x -> 0x%x (digest matches)", len, - src_addr, dst_addr)); - return true; - } - LOG(LL_DEBUG, - ("Copy %u @ 0x%x -> 0x%x (%s)", len, src_addr, dst_addr, cs_hex)); - struct esp_flash_write_ctx wctx; - if (!esp_init_flash_write_ctx(&wctx, dst_addr, len)) { - return false; - } - uint32_t offset = 0; - while (offset < len) { - uint32_t read_buf[128]; - int to_read = sizeof(read_buf); - if (offset + to_read > len) to_read = len - offset; - if (spi_flash_read(src_addr + offset, read_buf, to_read) != 0) { - LOG(LL_ERROR, ("Failed to read %d @ 0x%x", to_read, src_addr + offset)); - return false; - } - int num_written = - esp_flash_write(&wctx, mg_mk_str_n((const char *) read_buf, to_read)); - if (num_written < 0) return false; - if (num_written != to_read) { - /* Flush last chunk */ - int to_write = to_read - num_written; - num_written = esp_flash_write( - &wctx, - mg_mk_str_n(((const char *) read_buf) + num_written, to_write)); - if (num_written != to_write) return false; - } - offset += to_read; - mgos_wdt_feed(); - } - if (!verify_checksum(dst_addr, len, cs_hex, true)) { - return false; - } - return true; -} - -int mgos_upd_create_snapshot() { - struct slot_info rsi, wsi; - struct mgos_upd_boot_state bs; - if (!mgos_upd_boot_get_state(&bs)) return -1; - int inactive_slot = (bs.active_slot == 0 ? 1 : 0); - get_slot_info(bs.active_slot, &rsi); - get_slot_info(inactive_slot, &wsi); - LOG(LL_INFO, ("Snapshot: %d -> %d, " - "FW: 0x%x (%u) -> 0x%x, FS: 0x%x (%u) -> 0x%x", - rsi.id, wsi.id, rsi.fw_addr, rsi.fw_size, wsi.fw_addr, - rsi.fs_addr, rsi.fs_size, wsi.fs_addr)); - if (!copy_region(rsi.fw_addr, wsi.fw_addr, rsi.fw_size)) return -2; - if (!copy_region(rsi.fs_addr, wsi.fs_addr, rsi.fs_size)) return -3; - int slot = wsi.id; - rboot_config *cfg = get_rboot_config(); - cfg->roms[slot] = wsi.fw_addr; - cfg->roms_sizes[slot] = rsi.fw_size; - cfg->fs_addresses[slot] = wsi.fs_addr; - cfg->fs_sizes[slot] = rsi.fs_size; - if (!rboot_set_config(cfg)) return -4; - LOG(LL_INFO, ("Snapshot created")); - return slot; -} - -bool mgos_upd_boot_get_state(struct mgos_upd_boot_state *bs) { - rboot_config *cfg = get_rboot_config(); - if (cfg == NULL) return false; - LOG(LL_DEBUG, ("cur %d prev %d fwu %d", cfg->current_rom, cfg->previous_rom, - cfg->fw_updated)); - memset(bs, 0, sizeof(*bs)); - bs->active_slot = cfg->current_rom; - bs->revert_slot = cfg->previous_rom; - bs->is_committed = !cfg->fw_updated; - return true; -} - -bool mgos_upd_boot_set_state(const struct mgos_upd_boot_state *bs) { - rboot_config *cfg = get_rboot_config(); - if (cfg == NULL) return false; - if (bs->active_slot < 0 || bs->active_slot > 1 || bs->revert_slot < 0 || - bs->revert_slot > 1) { - return false; - } - cfg->current_rom = bs->active_slot; - cfg->previous_rom = bs->revert_slot; - cfg->fw_updated = cfg->is_first_boot = (!bs->is_committed); - cfg->boot_attempts = 0; - cfg->user_flags = 0; - LOG(LL_INFO, ("cur %d prev %d fwu %d", cfg->current_rom, cfg->previous_rom, - cfg->fw_updated)); - return rboot_set_config(cfg); -} - -void mgos_upd_boot_commit() { - struct mgos_upd_boot_state s; - if (!mgos_upd_boot_get_state(&s)) return; - if (s.is_committed) return; - LOG(LL_INFO, ("Committing ROM %d", s.active_slot)); - s.is_committed = true; - mgos_upd_boot_set_state(&s); -} - -void mgos_upd_boot_revert(void) { - struct mgos_upd_boot_state s; - if (!mgos_upd_boot_get_state(&s)) return; - if (s.is_committed) return; - s.active_slot = (s.active_slot == 0 ? 1 : 0); - LOG(LL_INFO, ("Update failed, reverting to ROM %d", s.active_slot)); - s.is_committed = true; - mgos_upd_boot_set_state(&s); -} - -bool mgos_upd_is_first_boot(void) { - return get_rboot_config()->is_first_boot; -} diff --git a/libs/ota-common/src/mgos_updater_common.c b/libs/ota-common/src/mgos_updater_common.c deleted file mode 100644 index d0d3194..0000000 --- a/libs/ota-common/src/mgos_updater_common.c +++ /dev/null @@ -1,887 +0,0 @@ -/* - * Copyright (c) 2014-2018 Cesanta Software Limited - * All rights reserved - * - * Licensed under the Apache License, Version 2.0 (the ""License""); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an ""AS IS"" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mgos_updater_common.h" - -#include -#include - -#include "common/cs_crc32.h" -#include "common/cs_dbg.h" -#include "common/cs_file.h" -#include "common/str_util.h" - -#include "mgos_event.h" -#include "mgos_hal.h" -#include "mgos_sys_config.h" -#include "mgos_timers.h" -#include "mgos_updater_hal.h" -#include "mgos_vfs.h" - -/* - * Using static variable (not only c->user_data), it allows to check if update - * already in progress when another request arrives - */ -struct update_context *s_ctx = NULL; - -/* Must be provided externally, usually auto-generated. */ -extern const char *build_id; -extern const char *build_version; - -#define UPDATER_CTX_FILE_NAME "updater.dat" -#define MANIFEST_FILENAME "manifest.json" -#define SHA1SUM_LEN 40 -#define PROGRESS_REPORT_BYTES 50000 -#define PROGRESS_REPORT_SECONDS 5 - -/* - * --- Zip file local header structure --- - * size offset - * local file header signature (0x04034b50) 4 0 - * version needed to extract 2 4 - * general purpose bit flag 2 6 - * compression method 2 8 - * last mod file time 2 10 - * last mod file date 2 12 - * crc-32 4 14 - * compressed size 4 18 - * uncompressed size 4 22 - * file name length 2 26 - * extra field length 2 28 - * file name (variable size) v 30 - * extra field (variable size) v - */ - -#define ZIP_LOCAL_HDR_SIZE 30U -#define ZIP_GENFLAG_OFFSET 6U -#define ZIP_COMPRESSION_METHOD_OFFSET 8U -#define ZIP_CRC32_OFFSET 14U -#define ZIP_COMPRESSED_SIZE_OFFSET 18U -#define ZIP_UNCOMPRESSED_SIZE_OFFSET 22U -#define ZIP_FILENAME_LEN_OFFSET 26U -#define ZIP_EXTRAS_LEN_OFFSET 28U -#define ZIP_FILENAME_OFFSET 30U -#define ZIP_FILE_DESCRIPTOR_SIZE 12U - -const uint32_t c_zip_file_header_magic = 0x04034b50; -const uint32_t c_zip_cdir_magic = 0x02014b50; - -enum update_state { - US_INITED = 0, - US_WAITING_MANIFEST_HEADER, - US_WAITING_MANIFEST, - US_WAITING_FILE_HEADER, - US_WAITING_FILE, - US_SKIPPING_DATA, - US_SKIPPING_DESCRIPTOR, - US_WRITE_FINISHED, - US_FINALIZE, - US_FINISHED, -}; - -#define MIN(a, b) ((a) < (b) ? (a) : (b)) - -static void mgos_upd_trigger_ota_event(void) { - struct mgos_ota_status s; - mgos_upd_get_status(&s); - mgos_event_trigger(MGOS_EVENT_OTA_STATUS, &s); -} - -static void updater_abort(void *arg) { - struct update_context *ctx = (struct update_context *) arg; - if (s_ctx != ctx) return; - LOG(LL_ERROR, ("Update timed out")); - /* Note that we do not free the context here, because whatever process - * is stuck may still be referring to it. We close the network connection, - * if there is one, to hopefully get things to wind down cleanly. */ - if (ctx->nc) ctx->nc->flags |= MG_F_CLOSE_IMMEDIATELY; - ctx->wdt = MGOS_INVALID_TIMER_ID; - s_ctx = NULL; -} - -struct update_context *updater_context_create(int timeout) { - if (s_ctx != NULL) { - LOG(LL_ERROR, ("%s", "Update already in progress")); - return NULL; - } - - struct mgos_upd_boot_state st; - if (!mgos_upd_boot_get_state(&st)) { - LOG(LL_ERROR, ("%s", "OTA is not supported")); - return NULL; - } - - if (!mgos_upd_is_committed()) { - LOG(LL_ERROR, ("%s", "Previous update has not been committed yet")); - return NULL; - } - - s_ctx = calloc(1, sizeof(*s_ctx)); - if (s_ctx == NULL) { - LOG(LL_ERROR, ("Out of memory")); - return NULL; - } - - s_ctx->dev_ctx = mgos_upd_hal_ctx_create(); - - if (timeout <= 0) timeout = mgos_sys_config_get_update_timeout(); - s_ctx->wdt_timeout_ms = timeout * 1000; - s_ctx->wdt = mgos_set_timer(s_ctx->wdt_timeout_ms, 0, updater_abort, s_ctx); - LOG(LL_INFO, ("starting, timeout %d", timeout)); - s_ctx->ota_state = MGOS_OTA_STATE_PROGRESS; - return s_ctx; -} - -struct update_context *updater_context_get_current(void) { - return s_ctx; -} - -void updater_set_status(struct update_context *ctx, enum update_state st) { - LOG(LL_DEBUG, ("Update state %d -> %d", (int) ctx->update_state, (int) st)); - ctx->update_state = st; -} - -/* - * During its work, updater requires requires to store some data. - * For example, manifest file, zip header - must be received fully, while - * content FW/FS files can be flashed directly from recv_mbuf - * To avoid extra memory usage, context contains plain pointer (*data) - * and mbuf (unprocessed); data is storing in memory only if where is no way - * to process it right now. - */ -static void context_update(struct update_context *ctx, const char *data, - size_t len) { - if (ctx->unprocessed.len != 0) { - /* We have unprocessed data, concatenate them with arrived */ - mbuf_append(&ctx->unprocessed, data, len); - ctx->data = ctx->unprocessed.buf; - ctx->data_len = ctx->unprocessed.len; - } else { - /* No unprocessed, trying to process directly received data */ - ctx->data = data; - ctx->data_len = len; - } -} - -static void context_save_unprocessed(struct update_context *ctx) { - if (ctx->unprocessed.len == 0) { - mbuf_append(&ctx->unprocessed, ctx->data, ctx->data_len); - ctx->data = ctx->unprocessed.buf; - ctx->data_len = ctx->unprocessed.len; - } -} - -void context_remove_data(struct update_context *ctx, size_t len) { - if (ctx->unprocessed.len != 0) { - /* Consumed data from unprocessed*/ - mbuf_remove(&ctx->unprocessed, len); - ctx->data = ctx->unprocessed.buf; - ctx->data_len = ctx->unprocessed.len; - } else { - /* Consumed received data */ - ctx->data = ctx->data + len; - ctx->data_len -= len; - } -} - -static void context_clear_current_file(struct update_context *ctx) { - memset(&ctx->info.current_file, 0, sizeof(ctx->info.current_file)); - ctx->current_file_crc = ctx->current_file_crc_calc = 0; - ctx->current_file_has_descriptor = false; -} - -int is_write_finished(struct update_context *ctx) { - return ctx->update_state == US_WRITE_FINISHED; -} - -int is_update_finished(struct update_context *ctx) { - return ctx->update_state == US_FINISHED; -} - -int is_reboot_required(struct update_context *ctx) { - return ctx->need_reboot; -} - -static int parse_zip_file_header(struct update_context *ctx) { - if (ctx->data_len < ZIP_LOCAL_HDR_SIZE) { - LOG(LL_DEBUG, ("Zip header is incomplete")); - /* Need more data*/ - return 0; - } - - if (memcmp(ctx->data, &c_zip_file_header_magic, 4) != 0) { - ctx->status_msg = "Malformed archive (invalid file header)"; - return -1; - } - - uint16_t file_name_len, extras_len; - memcpy(&file_name_len, ctx->data + ZIP_FILENAME_LEN_OFFSET, - sizeof(file_name_len)); - memcpy(&extras_len, ctx->data + ZIP_EXTRAS_LEN_OFFSET, sizeof(extras_len)); - - LOG(LL_DEBUG, ("Filename len = %d bytes, extras len = %d bytes", - (int) file_name_len, (int) extras_len)); - if (ctx->data_len < ZIP_LOCAL_HDR_SIZE + file_name_len + extras_len) { - /* Still need mode data */ - return 0; - } - - uint16_t compression_method; - memcpy(&compression_method, ctx->data + ZIP_COMPRESSION_METHOD_OFFSET, - sizeof(compression_method)); - - LOG(LL_DEBUG, ("Compression method=%d", (int) compression_method)); - if (compression_method != 0) { - /* Do not support compressed archives */ - ctx->status_msg = "Cannot handle compressed .zip"; - LOG(LL_ERROR, ("%s", ctx->status_msg)); - return -1; - } - - int i; - char *nodir_file_name = (char *) ctx->data + ZIP_FILENAME_OFFSET; - uint16_t nodir_file_name_len = file_name_len; - LOG(LL_DEBUG, - ("File name: %.*s", (int) nodir_file_name_len, nodir_file_name)); - - for (i = 0; i < file_name_len; i++) { - /* archive may contain folder, but we skip it, using filenames only */ - if (*(ctx->data + ZIP_FILENAME_OFFSET + i) == '/') { - nodir_file_name = (char *) ctx->data + ZIP_FILENAME_OFFSET + i + 1; - nodir_file_name_len -= (i + 1); - break; - } - } - - LOG(LL_DEBUG, - ("File name to use: %.*s", (int) nodir_file_name_len, nodir_file_name)); - - if (nodir_file_name_len >= sizeof(ctx->info.current_file.name)) { - /* We are in charge of file names, right? */ - ctx->status_msg = "Too long file name"; - LOG(LL_ERROR, ("%s", ctx->status_msg)); - return -1; - } - memcpy(ctx->info.current_file.name, nodir_file_name, nodir_file_name_len); - - memcpy(&ctx->info.current_file.size, ctx->data + ZIP_COMPRESSED_SIZE_OFFSET, - sizeof(ctx->info.current_file.size)); - - uint32_t uncompressed_size; - memcpy(&uncompressed_size, ctx->data + ZIP_UNCOMPRESSED_SIZE_OFFSET, - sizeof(uncompressed_size)); - - if (ctx->info.current_file.size != uncompressed_size) { - /* Probably malformed archive*/ - LOG(LL_ERROR, ("Malformed archive")); - ctx->status_msg = "Malformed archive"; - return -1; - } - - LOG(LL_DEBUG, ("File size: %u", (unsigned int) ctx->info.current_file.size)); - - uint16_t gen_flag; - memcpy(&gen_flag, ctx->data + ZIP_GENFLAG_OFFSET, sizeof(gen_flag)); - ctx->current_file_has_descriptor = ((gen_flag & (1 << 3)) != 0); - - LOG(LL_DEBUG, ("General flag=%d", (int) gen_flag)); - - memcpy(&ctx->current_file_crc, ctx->data + ZIP_CRC32_OFFSET, - sizeof(ctx->current_file_crc)); - - LOG(LL_DEBUG, ("CRC32: 0x%08x", (unsigned int) ctx->current_file_crc)); - - context_remove_data(ctx, ZIP_LOCAL_HDR_SIZE + file_name_len + extras_len); - - return 1; -} - -static int parse_manifest(struct update_context *ctx) { - struct mgos_upd_info *info = &ctx->info; - ctx->manifest_data = calloc(1, info->current_file.size); - if (ctx->manifest_data == NULL) { - ctx->status_msg = "Out of memory"; - return -1; - } - memcpy(ctx->manifest_data, ctx->data, info->current_file.size); - - if (json_scanf( - ctx->manifest_data, info->current_file.size, - "{name: %T, platform: %T, version: %T, build_id: %T, parts: %T}", - &info->name, &info->platform, &info->version, &info->build_id, - &info->parts) <= 0) { - ctx->status_msg = "Failed to parse manifest"; - return -1; - } - - if (info->platform.len == 0 || info->version.len == 0 || - info->build_id.len == 0 || info->parts.len == 0) { - ctx->status_msg = "Required manifest field missing"; - return -1; - } - - LOG(LL_INFO, - ("FW: %.*s %.*s %s %s -> %.*s %.*s", (int) info->name.len, info->name.ptr, - (int) info->platform.len, info->platform.ptr, build_version, build_id, - (int) info->version.len, info->version.ptr, (int) info->build_id.len, - info->build_id.ptr)); - - context_remove_data(ctx, info->current_file.size); - - return 1; -} - -static int finalize_write(struct update_context *ctx, struct mg_str tail) { - /* We have to add the tail to CRC now to be able to verify it. */ - if (tail.len > 0) { - ctx->current_file_crc_calc = cs_crc32(ctx->current_file_crc_calc, - (const uint8_t *) tail.p, tail.len); - } - - if (ctx->current_file_crc != 0 && - ctx->current_file_crc != ctx->current_file_crc_calc) { - LOG(LL_ERROR, ("Invalid CRC, want 0x%x, got 0x%x", - (unsigned int) ctx->current_file_crc, - (unsigned int) ctx->current_file_crc_calc)); - ctx->status_msg = "Invalid CRC"; - return -1; - } - - int ret = mgos_upd_file_end(ctx->dev_ctx, &ctx->info.current_file, tail); - if (ret != (int) tail.len) { - if (ret < 0) { - ctx->status_msg = mgos_upd_get_status_msg(ctx->dev_ctx); - } else { - ctx->status_msg = "Not all data was processed"; - ret = -1; - } - return ret; - } - - context_remove_data(ctx, tail.len); - - return 1; -} - -static void mgos_updater_progress(struct update_context *ctx) { - if (ctx->last_reported_bytes == 0 || - ctx->bytes_already_downloaded - ctx->last_reported_bytes >= - PROGRESS_REPORT_BYTES || - mg_time() - ctx->last_reported_time > PROGRESS_REPORT_SECONDS) { - if (ctx->zip_file_size > 0) { - float ratio = - (float) ctx->bytes_already_downloaded * 100.0f / ctx->zip_file_size; - LOG(LL_INFO, - ("%.2f%% total, %s %d of %d", ratio, ctx->info.current_file.name, - (int) ctx->info.current_file.processed, - (int) ctx->info.current_file.size)); - } else { - LOG(LL_INFO, ("%s %d of %d", ctx->info.current_file.name, - (int) ctx->info.current_file.processed, - (int) ctx->info.current_file.size)); - } - /* If progress has been made, reset the update WDT. */ - if (ctx->last_reported_bytes != ctx->bytes_already_downloaded) { - mgos_clear_timer(ctx->wdt); - ctx->wdt = mgos_set_timer(ctx->wdt_timeout_ms, 0, updater_abort, ctx); - } - ctx->last_reported_bytes = ctx->bytes_already_downloaded; - ctx->last_reported_time = mg_time(); - mgos_upd_trigger_ota_event(); - } -} - -static int updater_process_int(struct update_context *ctx, const char *data, - size_t len) { - int ret; - if (len != 0) { - context_update(ctx, data, len); - } - - while (true) { - switch (ctx->update_state) { - case US_INITED: { - updater_set_status(ctx, US_WAITING_MANIFEST_HEADER); - } /* fall through */ - case US_WAITING_MANIFEST_HEADER: { - if ((ret = parse_zip_file_header(ctx)) <= 0) { - if (ret == 0) { - context_save_unprocessed(ctx); - } - return ret; - } - if (strncmp(ctx->info.current_file.name, MANIFEST_FILENAME, - sizeof(MANIFEST_FILENAME)) != 0) { - /* We've got file header, but it isn't not metadata */ - LOG(LL_ERROR, ("Get %s instead of %s", ctx->info.current_file.name, - MANIFEST_FILENAME)); - return -1; - } - updater_set_status(ctx, US_WAITING_MANIFEST); - } /* fall through */ - case US_WAITING_MANIFEST: { - /* - * Assume metadata isn't too big and might be cached - * otherwise we need streaming json-parser - */ - if (ctx->data_len < ctx->info.current_file.size) { - context_save_unprocessed(ctx); - return 0; - } - - if (ctx->current_file_crc != 0 && - cs_crc32(0, (const uint8_t *) ctx->data, - ctx->info.current_file.size) != ctx->current_file_crc) { - ctx->status_msg = "Invalid CRC"; - return -1; - } - - if ((ret = parse_manifest(ctx)) < 0) return ret; - - if (strncasecmp(ctx->info.platform.ptr, - CS_STRINGIFY_MACRO(FW_ARCHITECTURE), - strlen(CS_STRINGIFY_MACRO(FW_ARCHITECTURE))) != 0) { - LOG(LL_ERROR, - ("Wrong platform: want \"%s\", got \"%s\"", - CS_STRINGIFY_MACRO(FW_ARCHITECTURE), ctx->info.platform.ptr)); - ctx->status_msg = "Wrong platform"; - ctx->ota_state = MGOS_OTA_STATE_ERROR; - return -1; - } - - if (ctx->ignore_same_version && - strncmp(ctx->info.version.ptr, build_version, - ctx->info.version.len) == 0 && - strncmp(ctx->info.build_id.ptr, build_id, ctx->info.build_id.len) == - 0) { - ctx->status_msg = "Version is the same as current"; - return 1; - } - - if ((ret = mgos_upd_begin(ctx->dev_ctx, &ctx->info.parts)) < 0) { - ctx->status_msg = mgos_upd_get_status_msg(ctx->dev_ctx); - LOG(LL_ERROR, ("Bad manifest: %d %s", ret, ctx->status_msg)); - return ret; - } - - ctx->info.abort = false; - mgos_event_trigger(MGOS_EVENT_OTA_BEGIN, &ctx->info); - if (ctx->info.abort) { - ctx->status_msg = "OTA aborted by the MGOS_EVENT_OTA_BEGIN handler"; - return -1; - } - - context_clear_current_file(ctx); - updater_set_status(ctx, US_WAITING_FILE_HEADER); - } /* fall through */ - case US_WAITING_FILE_HEADER: { - if (ctx->data_len < 4) { - context_save_unprocessed(ctx); - return 0; - } - if (memcmp(ctx->data, &c_zip_cdir_magic, 4) == 0) { - LOG(LL_INFO, ("Reached the end of archive")); - updater_set_status(ctx, US_WRITE_FINISHED); - break; - } - if ((ret = parse_zip_file_header(ctx)) <= 0) { - if (ret == 0) context_save_unprocessed(ctx); - return ret; - } - - enum mgos_upd_file_action r = - mgos_upd_file_begin(ctx->dev_ctx, &ctx->info.current_file); - - if (r == MGOS_UPDATER_ABORT) { - ctx->status_msg = mgos_upd_get_status_msg(ctx->dev_ctx); - return -1; - } else if (r == MGOS_UPDATER_SKIP_FILE) { - updater_set_status(ctx, US_SKIPPING_DATA); - break; - } - updater_set_status(ctx, US_WAITING_FILE); - ctx->current_file_crc_calc = 0; - ctx->last_reported_bytes = 0; - } /* fall through */ - case US_WAITING_FILE: { - while (true) { - struct mg_str to_process = { - .p = ctx->data, - .len = MIN(MIN(ctx->info.current_file.size - - ctx->info.current_file.processed, - ctx->data_len), - MGOS_UPDATER_DATA_CHUNK_SIZE), - }; - if (to_process.len < MGOS_UPDATER_DATA_CHUNK_SIZE) break; - int num_processed = mgos_upd_file_data( - ctx->dev_ctx, &ctx->info.current_file, to_process); - if (num_processed < 0) { - ctx->status_msg = mgos_upd_get_status_msg(ctx->dev_ctx); - return num_processed; - } else if (num_processed > 0) { - ctx->current_file_crc_calc = - cs_crc32(ctx->current_file_crc_calc, - (const uint8_t *) to_process.p, num_processed); - context_remove_data(ctx, num_processed); - ctx->info.current_file.processed += num_processed; - } else { - break; - } - } - mgos_updater_progress(ctx); - - uint32_t bytes_left = - ctx->info.current_file.size - ctx->info.current_file.processed; - if (bytes_left > ctx->data_len) { - context_save_unprocessed(ctx); - return 0; - } - - struct mg_str tail = {.p = ctx->data, .len = bytes_left}; - assert(tail.len < MGOS_UPDATER_DATA_CHUNK_SIZE); - - if (finalize_write(ctx, tail) < 0) { - return -1; - } - context_clear_current_file(ctx); - updater_set_status(ctx, US_WAITING_FILE_HEADER); - break; - } - case US_SKIPPING_DATA: { - uint32_t to_skip = - MIN(ctx->data_len, - ctx->info.current_file.size - ctx->info.current_file.processed); - ctx->info.current_file.processed += to_skip; - context_remove_data(ctx, to_skip); - mgos_updater_progress(ctx); - - if (ctx->info.current_file.processed < ctx->info.current_file.size) { - context_save_unprocessed(ctx); - return 0; - } - - context_clear_current_file(ctx); - updater_set_status(ctx, US_SKIPPING_DESCRIPTOR); - } /* fall through */ - case US_SKIPPING_DESCRIPTOR: { - bool has_descriptor = ctx->current_file_has_descriptor; - LOG(LL_DEBUG, ("Has descriptor : %d", has_descriptor)); - context_clear_current_file(ctx); - ctx->current_file_has_descriptor = false; - if (has_descriptor) { - /* If file has descriptor we have to skip 12 bytes after its body */ - ctx->info.current_file.size = ZIP_FILE_DESCRIPTOR_SIZE; - updater_set_status(ctx, US_SKIPPING_DATA); - } else { - updater_set_status(ctx, US_WAITING_FILE_HEADER); - } - - context_save_unprocessed(ctx); - break; - } - case US_WRITE_FINISHED: { - /* We will stay in this state until explicitly finalized. */ - return 0; - } - case US_FINALIZE: { - ret = 1; - ctx->status_msg = "Update applied, finalizing"; - if (ctx->fctx.commit_timeout > 0) { - if (!mgos_upd_set_commit_timeout(ctx->fctx.commit_timeout)) { - ctx->status_msg = "Cannot save update status"; - return -1; - } - } - if ((ret = mgos_upd_finalize(ctx->dev_ctx)) < 0) { - ctx->status_msg = mgos_upd_get_status_msg(ctx->dev_ctx); - return ret; - } - ctx->result = 1; - ctx->need_reboot = 1; - updater_finish(ctx); - break; - } - case US_FINISHED: { - /* After receiving manifest, fw & fs just skipping all data */ - context_remove_data(ctx, ctx->data_len); - if (ctx->result_cb != NULL) { - ctx->result_cb(ctx); - ctx->result_cb = NULL; - } - return ctx->result; - } - } - } -} - -int updater_process(struct update_context *ctx, const char *data, size_t len) { - ctx->bytes_already_downloaded += len; - ctx->result = updater_process_int(ctx, data, len); - if (ctx->result != 0) { - updater_finish(ctx); - } - return ctx->result; -} - -int updater_finalize(struct update_context *ctx) { - if (ctx->update_state == US_FINISHED) { - return -1; - } else if (ctx->update_state != US_WRITE_FINISHED) { - if (ctx->status_msg == NULL) ctx->status_msg = "Wrong state for finalize"; - return -1; - } - updater_set_status(ctx, US_FINALIZE); - return updater_process(ctx, NULL, 0); -} - -void updater_finish(struct update_context *ctx) { - updater_set_status(ctx, US_FINISHED); - ctx->ota_state = - ctx->result == 1 ? MGOS_OTA_STATE_SUCCESS : MGOS_OTA_STATE_ERROR; - mgos_upd_trigger_ota_event(); - if (ctx->update_state == US_FINISHED) return; - updater_process_int(ctx, NULL, 0); -} - -void updater_context_free(struct update_context *ctx) { - if (!is_update_finished(ctx)) { - LOG(LL_ERROR, ("Update terminated unexpectedly")); - } - if (ctx == s_ctx) s_ctx = NULL; - mgos_clear_timer(ctx->wdt); - mgos_upd_hal_ctx_free(ctx->dev_ctx); - mbuf_free(&ctx->unprocessed); - free(ctx->manifest_data); - free(ctx); -} - -void bin2hex(const uint8_t *src, int src_len, char *dst) { - int i = 0; - for (i = 0; i < src_len; i++) { - sprintf(dst, "%02x", (int) *src); - dst += 2; - src += 1; - } -} - -static bool file_copy(const char *old_path, const char *new_path, - const char *name, char tmp_name[MG_MAX_PATH]) { - bool ret = false; - FILE *old_f = NULL, *new_f = NULL; - struct stat st; - int readen, to_read = 0, total = 0; - - LOG(LL_INFO, ("Copying %s", name)); - - sprintf(tmp_name, "%s/%s", old_path, name); - old_f = fopen(tmp_name, "r"); - if (old_f == NULL) { - LOG(LL_ERROR, ("Failed to open %s for reading", tmp_name)); - goto out; - } - if (stat(tmp_name, &st) != 0) { - LOG(LL_ERROR, ("Cannot get previous %s size", tmp_name)); - goto out; - } - - sprintf(tmp_name, "%s/%s", new_path, name); - new_f = fopen(tmp_name, "w"); - if (new_f == NULL) { - LOG(LL_ERROR, ("Failed to open %s for writing", tmp_name)); - goto out; - } - - char buf[128]; - to_read = MIN(sizeof(buf), (size_t) st.st_size); - while (to_read != 0) { - if ((readen = fread(buf, 1, to_read, old_f)) < 0) { - LOG(LL_ERROR, ("Failed to read %d bytes from %s", to_read, name)); - goto out; - } - - if (fwrite(buf, 1, readen, new_f) != (size_t) readen) { - LOG(LL_ERROR, ("Failed to write %d bytes to %s", readen, name)); - goto out; - } - - total += readen; - to_read = MIN(sizeof(buf), (size_t)(st.st_size - total)); - } - - LOG(LL_DEBUG, ("Wrote %d to %s", total, tmp_name)); - - ret = true; - -out: - if (old_f != NULL) fclose(old_f); - if (new_f != NULL) { - fclose(new_f); - if (!ret) remove(tmp_name); - } - return ret; -} - -bool mgos_upd_merge_fs(const char *old_fs_path, const char *new_fs_path) { - bool ret = false; - DIR *dir = opendir(old_fs_path); - if (dir == NULL) { - LOG(LL_ERROR, ("Failed to open root directory")); - goto out; - } - - struct dirent *de; - while ((de = readdir(dir)) != NULL) { - struct stat st; - char tmp_name[MG_MAX_PATH + 1]; - sprintf(tmp_name, "%s/%s", new_fs_path, de->d_name); - if (stat(tmp_name, &st) != 0) { - /* File not found on the new fs, copy. */ - if (!file_copy(old_fs_path, new_fs_path, de->d_name, tmp_name)) { - LOG(LL_ERROR, ("Failed to copy %s", de->d_name)); - goto out; - } - } - mgos_wdt_feed(); - } - ret = true; - -out: - if (dir != NULL) closedir(dir); - return ret; -} - -bool mgos_upd_commit() { - if (mgos_upd_is_committed()) return false; - mgos_upd_boot_commit(); - remove(UPDATER_CTX_FILE_NAME); - mgos_upd_trigger_ota_event(); - return true; -} - -bool mgos_upd_is_committed() { - struct mgos_upd_boot_state s; - if (!mgos_upd_boot_get_state(&s)) return false; - return s.is_committed; -} - -bool mgos_upd_revert(bool reboot) { - if (mgos_upd_is_committed()) return false; - mgos_upd_boot_revert(); - if (reboot) mgos_system_restart(); - return true; -} - -void mgos_upd_watchdog_cb(void *arg) { - if (!mgos_upd_is_committed()) { - /* Timer fired and update has not been committed. Revert! */ - LOG(LL_ERROR, ("Update commit timeout expired")); - mgos_upd_revert(true /* reboot */); - } - (void) arg; -} - -int mgos_upd_get_commit_timeout() { - size_t len; - char *data = cs_read_file(UPDATER_CTX_FILE_NAME, &len); - if (data == NULL) return 0; - struct update_file_context *fctx = (struct update_file_context *) data; - LOG(LL_INFO, ("Update state: %d", fctx->commit_timeout)); - int res = fctx->commit_timeout; - free(data); - return res; -} - -bool mgos_upd_set_commit_timeout(int commit_timeout) { - bool ret = false; - LOG(LL_DEBUG, ("Writing update state to %s", UPDATER_CTX_FILE_NAME)); - FILE *fp = fopen(UPDATER_CTX_FILE_NAME, "w"); - if (fp == NULL) return false; - struct update_file_context fctx; - fctx.commit_timeout = commit_timeout; - if (fwrite(&fctx, sizeof(fctx), 1, fp) == 1) { - ret = true; - } - fclose(fp); - return ret; -} - -void mgos_upd_boot_finish(bool is_successful, bool is_first) { - /* - * If boot is not successful, there's only one thing to do: - * revert update (if any) and reboot. - * If this was the first boot after an update, this will revert it. - */ - LOG(LL_DEBUG, ("%d %d", is_successful, is_first)); - if (!is_first) return; - if (!is_successful) { - mgos_upd_revert(true /* reboot */); - /* Not reached */ - return; - } - /* We booted. Now see if we have any special instructions. */ - int commit_timeout = mgos_upd_get_commit_timeout(); - if (commit_timeout > 0) { - LOG(LL_INFO, ("Arming commit watchdog for %d seconds", commit_timeout)); - mgos_set_timer(commit_timeout * 1000, 0 /* repeat */, mgos_upd_watchdog_cb, - NULL); - } else { - mgos_upd_commit(); - } - mgos_upd_trigger_ota_event(); -} - -const char *mgos_ota_state_str(enum mgos_ota_state state) { - switch (state) { - case MGOS_OTA_STATE_IDLE: - return "idle"; - case MGOS_OTA_STATE_PROGRESS: - return "progress"; - case MGOS_OTA_STATE_ERROR: - return "error"; - case MGOS_OTA_STATE_SUCCESS: - return "success"; - } - return ""; -} - -/* For FFI */ -const char *mgos_ota_status_get_msg(struct mgos_ota_status *s) { - return s->msg; -} - -bool mgos_upd_get_status(struct mgos_ota_status *s) { - struct mgos_upd_boot_state bs; - memset(s, 0, sizeof(*s)); - if (s_ctx != NULL) { - s->state = s_ctx->ota_state; - s->msg = s_ctx->status_msg; - if (s_ctx->zip_file_size > 0) { - s->progress_percent = - s_ctx->bytes_already_downloaded * 100.0 / s_ctx->zip_file_size; - } - } - if (s->msg == NULL) s->msg = mgos_ota_state_str(s->state); - if (!mgos_upd_boot_get_state(&bs)) return false; - s->is_committed = bs.is_committed; - s->partition = bs.active_slot; - s->commit_timeout = mgos_upd_get_commit_timeout(); - return true; -} - -bool mgos_ota_common_init(void) { - return true; -} diff --git a/libs/ota-common/src/stm32/stm32_updater.c b/libs/ota-common/src/stm32/stm32_updater.c deleted file mode 100644 index 3cd4f2e..0000000 --- a/libs/ota-common/src/stm32/stm32_updater.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Copyright (c) 2014-2018 Cesanta Software Limited - * All rights reserved - * - * Licensed under the Apache License, Version 2.0 (the ""License""); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an ""AS IS"" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "common/cs_crc32.h" -#include "common/cs_dbg.h" -#include "common/str_util.h" - -#include "frozen.h" - -#include "mgos_boot_cfg.h" -#include "mgos_vfs.h" -#include "mgos_vfs_dev.h" -#include "mgos_updater_hal.h" -#include "mgos_updater_util.h" - -#include "stm32_vfs_dev_flash.h" - -struct mgos_upd_hal_ctx { - const char *status_msg; - struct mgos_vfs_dev *app_dev, *fs_dev, *bl_dev; - struct mg_str app_file_name, app_cs_sha1; - unsigned int app_bl_size, app_bl_cfg_size, app_fs_size, update_bl; - struct mg_str fs_file_name, fs_cs_sha1; - uintptr_t app_org; - size_t file_offset, app_bl_cfg_offset, app_fs_offset, app_app_offset; - uint32_t app_len, app_crc32; - struct mgos_vfs_dev *cur_dev; - size_t cur_dev_num_erased; - int8_t dst_slot; -}; - -struct mgos_upd_hal_ctx *mgos_upd_hal_ctx_create(void) { - struct mgos_upd_hal_ctx *ctx = - (struct mgos_upd_hal_ctx *) calloc(1, sizeof(*ctx)); - return ctx; -} - -const char *mgos_upd_get_status_msg(struct mgos_upd_hal_ctx *ctx) { - return ctx->status_msg; -} - -int mgos_upd_begin(struct mgos_upd_hal_ctx *ctx, struct json_token *parts) { - const struct mgos_boot_cfg *bcfg = mgos_boot_cfg_get(); - if (bcfg == NULL) return -1; - struct json_token app_file_name = JSON_INVALID_TOKEN, - app_cs_sha1 = JSON_INVALID_TOKEN; - struct json_token fs_file_name = JSON_INVALID_TOKEN, - fs_cs_sha1 = JSON_INVALID_TOKEN; - json_scanf(parts->ptr, parts->len, - "{app: {src: %T, cs_sha1: %T, bl_size: %u, bl_cfg_size: %u, " - " fs_size: %u, update_bl: %B}, " - "fs: {src: %T, cs_sha1: %T}}", - &app_file_name, &app_cs_sha1, &ctx->app_bl_size, - &ctx->app_bl_cfg_size, &ctx->app_fs_size, &ctx->update_bl, - &fs_file_name, &fs_cs_sha1); - if (app_file_name.len == 0 || app_cs_sha1.len == 0 || - (ctx->update_bl && ctx->app_bl_size == 0)) { - ctx->status_msg = "Incomplete update package"; - return -2; - } - ctx->app_file_name = mg_mk_str_n(app_file_name.ptr, app_file_name.len); - ctx->app_cs_sha1 = mg_mk_str_n(app_cs_sha1.ptr, app_cs_sha1.len); - ctx->fs_file_name = mg_mk_str_n(fs_file_name.ptr, fs_file_name.len); - ctx->fs_cs_sha1 = mg_mk_str_n(fs_cs_sha1.ptr, fs_cs_sha1.len); - ctx->app_bl_cfg_offset = ctx->app_bl_size; - ctx->app_fs_offset = ctx->app_bl_cfg_offset + ctx->app_bl_cfg_size; - ctx->app_app_offset = ctx->app_fs_offset + ctx->app_fs_size; - ctx->app_org = FLASH_BASE + ctx->app_app_offset; - /* Try to put into a directly bootable slot, if possible. */ - ctx->dst_slot = - mgos_boot_cfg_find_slot(bcfg, ctx->app_org, true /* want_fs */, -1, -1); - if (ctx->dst_slot < 0) { - /* Ok, try any available slot, boot loader will perform a swap. */ - ctx->dst_slot = mgos_boot_cfg_find_slot(bcfg, 0 /* map_addr */, - true /* want_fs */, -1, -1); - } - if (ctx->dst_slot < 0) { - ctx->status_msg = "No slots available for update"; - return -3; - } - const struct mgos_boot_slot *sl = &bcfg->slots[ctx->dst_slot]; - LOG(LL_INFO, ("Picked slot %d, app -> %s, FS -> %s", ctx->dst_slot, - sl->cfg.app_dev, sl->cfg.fs_dev)); - ctx->app_dev = mgos_vfs_dev_open(sl->cfg.app_dev); - if (ctx->app_dev == NULL) { - ctx->status_msg = "Failed to open app_dev"; - return -4; - } - ctx->fs_dev = mgos_vfs_dev_open(sl->cfg.fs_dev); - if (ctx->fs_dev == NULL) { - ctx->status_msg = "Failed to open fs_dev"; - return -5; - } - if (ctx->update_bl) { - ctx->bl_dev = mgos_vfs_dev_create(MGOS_VFS_DEV_TYPE_STM32_FLASH, NULL); - if (ctx->bl_dev == NULL || - !stm32_flash_dev_init(ctx->bl_dev, 0, ctx->app_bl_size, - false /* ese */)) { - ctx->status_msg = "Failed to open bl_dev"; - return -6; - } - } - LOG(LL_INFO, ("BL size %u (update? %d) + %u cfg; FS size %u; app org 0x%lx", - ctx->app_bl_size, ctx->update_bl, ctx->app_bl_cfg_size, - ctx->app_fs_size, (unsigned long) ctx->app_org)); - /* To simplify logic while writing. */ - if (ctx->app_bl_size % MGOS_UPDATER_DATA_CHUNK_SIZE != 0 || - ctx->app_bl_cfg_size % MGOS_UPDATER_DATA_CHUNK_SIZE != 0 || - ctx->app_fs_size % MGOS_UPDATER_DATA_CHUNK_SIZE != 0) { - ctx->status_msg = "Invalid size"; - return -7; - } - return 1; -} - -enum mgos_upd_file_action mgos_upd_file_begin( - struct mgos_upd_hal_ctx *ctx, const struct mgos_upd_file_info *fi) { - enum mgos_upd_file_action res = MGOS_UPDATER_SKIP_FILE; - ctx->file_offset = 0; - if (mg_vcmp(&ctx->app_file_name, fi->name) == 0) { - if (fi->size < ctx->app_app_offset) { - ctx->status_msg = "App file too short"; - res = MGOS_UPDATER_ABORT; - goto out; - } - ctx->app_len = fi->size - ctx->app_app_offset; - res = MGOS_UPDATER_PROCESS_FILE; - } else if (mg_vcmp(&ctx->fs_file_name, fi->name) == 0) { - res = MGOS_UPDATER_PROCESS_FILE; - } -out: - return res; -} - -int mgos_upd_file_data(struct mgos_upd_hal_ctx *ctx, - const struct mgos_upd_file_info *fi, - struct mg_str data) { - int res = -1; - size_t write_offset = 0; - struct mgos_vfs_dev *dev = NULL; - if (mg_vcmp(&ctx->app_file_name, fi->name) == 0) { - if (ctx->file_offset < ctx->app_bl_size) { - /* Boot loader. Write if instructed. */ - if (ctx->update_bl) { - dev = ctx->bl_dev; - write_offset = ctx->file_offset; - } - } else if (ctx->file_offset < ctx->app_fs_offset) { - /* Boot loader config is never updated during OTA. */ - } else if (ctx->file_offset < ctx->app_app_offset) { - /* FS image - write unless we have a separate file. - * Generally speaking it should be one or the other, not both. */ - if (ctx->fs_file_name.len == 0) { - dev = ctx->fs_dev; - write_offset = ctx->file_offset - ctx->app_fs_offset; - } - } else { - /* This is app. */ - dev = ctx->app_dev; - write_offset = ctx->file_offset - ctx->app_app_offset; - ctx->app_crc32 = - cs_crc32(ctx->app_crc32, (const uint8_t *) data.p, data.len); - } - } else if (mg_vcmp(&ctx->fs_file_name, fi->name) == 0) { - dev = ctx->fs_dev; - } - if (dev != ctx->cur_dev) { - ctx->cur_dev = dev; - ctx->cur_dev_num_erased = 0; - } - LOG(LL_DEBUG, - ("fn %s ds %d fo %d | %d %d %d | dev %s wo %d", fi->name, (int) data.len, - (int) ctx->file_offset, (int) ctx->app_bl_size, (int) ctx->app_fs_offset, - (int) ctx->app_app_offset, (dev ? dev->name : "-"), (int) write_offset)); - if (dev != NULL) { - /* See if we need to erase before writing. */ - size_t write_end = write_offset + data.len; - if (write_end > ctx->cur_dev_num_erased) { - size_t erase_len = 0; - size_t erase_sizes[MGOS_VFS_DEV_NUM_ERASE_SIZES]; - size_t dev_size = mgos_vfs_dev_get_size(dev); - size_t headroom = dev_size - ctx->cur_dev_num_erased; - if (mgos_vfs_dev_get_erase_sizes(dev, erase_sizes) == 0) { - erase_len = write_end - ctx->cur_dev_num_erased; - /* Use the largest erase size smaller than the remaining space ahead. */ - for (int i = 0; i < (int) ARRAY_SIZE(erase_sizes); i++) { - if (erase_sizes[i] > 0 && erase_sizes[i] < headroom) { - erase_len = erase_sizes[i]; - } else { - break; - } - } - } else { - /* Just nuke the whole thing */ - erase_len = headroom; - } - LOG(LL_DEBUG, ("Erase %s %d @ 0x%lx", dev->name, (int) erase_len, - (unsigned long) ctx->cur_dev_num_erased)); - enum mgos_vfs_dev_err eres = - mgos_vfs_dev_erase(dev, ctx->cur_dev_num_erased, erase_len); - if (eres != 0) { - ctx->status_msg = "Erase failed"; - LOG(LL_INFO, - ("%s: erase %d failed: %d", dev->name, (int) erase_len, eres)); - goto out; - } - ctx->cur_dev_num_erased += erase_len; - } - enum mgos_vfs_dev_err wres = - mgos_vfs_dev_write(dev, write_offset, data.len, data.p); - if (wres < 0) { - LOG(LL_ERROR, - ("%s @ %d => wr %s: %d @ %d = %d", fi->name, (int) ctx->file_offset, - dev->name, (int) data.len, (int) write_offset, wres)); - res = wres; - } else { - res = (int) data.len; - } - } else { - res = (int) data.len; /* Skip */ - } -out: - ctx->file_offset += data.len; - return res; -} - -int mgos_upd_file_end(struct mgos_upd_hal_ctx *ctx, - const struct mgos_upd_file_info *fi, struct mg_str tail) { - int res = -1; - if (tail.len > 0) { - int wres = mgos_upd_file_data(ctx, fi, tail); - if (wres != (int) tail.len) { - res = wres; - goto out; - } - } - /* TODO(rojer): Verify SHA1. */ - res = (int) tail.len; -out: - return res; -} - -int mgos_upd_finalize(struct mgos_upd_hal_ctx *ctx) { - int res = -1; - struct mgos_boot_cfg *bcfg = mgos_boot_cfg_get(); - struct mgos_boot_slot_state *ss; - if (bcfg == NULL) goto out; - bcfg->revert_slot = bcfg->active_slot; - bcfg->active_slot = ctx->dst_slot; - bcfg->flags &= ~(MGOS_BOOT_F_COMMITTED); - bcfg->flags |= (MGOS_BOOT_F_FIRST_BOOT_A | MGOS_BOOT_F_FIRST_BOOT_B); - bcfg->flags |= (MGOS_BOOT_F_MERGE_FS); - ss = &bcfg->slots[ctx->dst_slot].state; - ss->app_len = ctx->app_len; - ss->app_org = ctx->app_org; - ss->app_crc32 = ctx->app_crc32; - ss->app_flags = 0; - LOG(LL_INFO, ("Updating boot config")); - res = (mgos_boot_cfg_write(bcfg, true /* dump */) ? 1 : -1); - -out: - return res; -} - -void mgos_upd_hal_ctx_free(struct mgos_upd_hal_ctx *ctx) { - if (ctx == NULL) return; - mgos_vfs_dev_close(ctx->app_dev); - mgos_vfs_dev_close(ctx->fs_dev); - mgos_vfs_dev_close(ctx->bl_dev); - free(ctx); -} - -int mgos_upd_create_snapshot() { - /* TODO */ - return -1; -} - -bool mgos_upd_boot_get_state(struct mgos_upd_boot_state *bs) { - const struct mgos_boot_cfg *bcfg = mgos_boot_cfg_get(); - if (bcfg == NULL) return false; - memset(bs, 0, sizeof(*bs)); - bs->active_slot = bcfg->active_slot; - bs->revert_slot = bcfg->revert_slot; - bs->is_committed = !!(bcfg->flags & MGOS_BOOT_F_COMMITTED); - return true; -} - -bool mgos_upd_boot_set_state(const struct mgos_upd_boot_state *bs) { - struct mgos_boot_cfg *bcfg = mgos_boot_cfg_get(); - if (bcfg == NULL) return false; - bcfg->active_slot = bs->active_slot; - bcfg->revert_slot = bs->revert_slot; - if (bs->is_committed) { - bcfg->flags |= MGOS_BOOT_F_COMMITTED; - } else { - bcfg->flags &= ~(MGOS_BOOT_F_COMMITTED); - } - return mgos_boot_cfg_write(bcfg, true /* dump */); -} - -int mgos_upd_apply_update(void) { - int res = -1; - struct mgos_vfs_dev *old_fs_dev = NULL; - const struct mgos_boot_cfg *bcfg = mgos_boot_cfg_get(); - if (bcfg == NULL) goto out; - if (bcfg->revert_slot < 0) { - LOG(LL_ERROR, ("Revert slot not set!")); - goto out; - } - if (!mgos_vfs_mount_dev_name("/old", - bcfg->slots[bcfg->revert_slot].cfg.fs_dev, - CS_STRINGIFY_MACRO(MGOS_ROOT_FS_TYPE), - CS_STRINGIFY_MACRO(MGOS_ROOT_FS_OPTS))) { - LOG(LL_ERROR, ("Failed to mount old file system")); - goto out; - } - res = (mgos_upd_merge_fs("/old", "/") ? 0 : -2); - mgos_vfs_umount("/old"); -out: - mgos_vfs_dev_close(old_fs_dev); - return res; -} - -static bool mgos_boot_commit_slot(struct mgos_boot_cfg *bcfg, int8_t slot) { - bcfg->active_slot = slot; - bcfg->revert_slot = -1; - bcfg->flags |= MGOS_BOOT_F_COMMITTED; - bcfg->flags &= ~(MGOS_BOOT_F_FIRST_BOOT_A | MGOS_BOOT_F_MERGE_FS); - return (mgos_boot_cfg_write(bcfg, false /* dump */)); -} - -void mgos_upd_boot_commit(void) { - struct mgos_boot_cfg *bcfg = mgos_boot_cfg_get(); - if (bcfg == NULL) return; - int8_t active_slot = bcfg->active_slot; - if (mgos_boot_commit_slot(bcfg, active_slot)) { - LOG(LL_INFO, ("Committed slot %d", active_slot)); - } -} - -void mgos_upd_boot_revert(void) { - struct mgos_boot_cfg *bcfg = mgos_boot_cfg_get(); - if (bcfg == NULL) return; - int8_t revert_slot = bcfg->revert_slot; - if (mgos_boot_commit_slot(bcfg, revert_slot)) { - LOG(LL_INFO, ("Reverted to slot %d", revert_slot)); - } -} - -bool mgos_upd_is_first_boot(void) { - struct mgos_boot_cfg *bcfg = mgos_boot_cfg_get(); - if (bcfg == NULL) return false; - return (bcfg->flags & MGOS_BOOT_F_FIRST_BOOT_A) != 0; -} diff --git a/libs/ota-http-client/LICENSE b/libs/ota-http-client/LICENSE deleted file mode 100644 index 19bf2e3..0000000 --- a/libs/ota-http-client/LICENSE +++ /dev/null @@ -1,14 +0,0 @@ -Copyright (c) 2018 Cesanta Software Limited -All rights reserved - -Licensed under the Apache License, Version 2.0 (the ""License""); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an ""AS IS"" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/libs/ota-http-client/README.md b/libs/ota-http-client/README.md deleted file mode 100644 index d88cdb5..0000000 --- a/libs/ota-http-client/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Implementation of Mongoose OS OTA HTTP client - -This library adds a device configuration section called `update`, where -a device could be configured to poll a specified HTTP URL for a new -app firmware. - -Also, this library adds a C API to fetch a new firmware from the given -URL and update programmatically. - -## Configuration section - -The library adds the following object to the device configuration: - - -```javascript - "update": { - "commit_timeout": 0, // OTA commit timeout - "url": "", // HTTP URL to poll - "interval": 0, // Polling interval - "ssl_ca_file": "ca.pem", // TLS CA cert file - "ssl_client_cert_file": "", // TLS cert file - "ssl_server_name": "", // TLS server name - "enable_post": true - } -``` - diff --git a/libs/ota-http-client/include/mgos_ota_http_client.h b/libs/ota-http-client/include/mgos_ota_http_client.h deleted file mode 100644 index 81095f7..0000000 --- a/libs/ota-http-client/include/mgos_ota_http_client.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2014-2018 Cesanta Software Limited - * All rights reserved - * - * Licensed under the Apache License, Version 2.0 (the ""License""); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an ""AS IS"" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CS_MOS_LIBS_OTA_HTTP_CLIENT_SRC_MGOS_OTA_HTTP_CLIENT_H_ -#define CS_MOS_LIBS_OTA_HTTP_CLIENT_SRC_MGOS_OTA_HTTP_CLIENT_H_ - -#include - -#include "mgos_updater_common.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* Start OTA update by pulling the firmware from the given URL. */ -void mgos_ota_http_start(struct update_context *ctx, const char *url); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CS_MOS_LIBS_OTA_HTTP_CLIENT_SRC_MGOS_OTA_HTTP_CLIENT_H_ */ diff --git a/libs/ota-http-client/mjs_fs/api_ota.js b/libs/ota-http-client/mjs_fs/api_ota.js deleted file mode 100644 index 98c6025..0000000 --- a/libs/ota-http-client/mjs_fs/api_ota.js +++ /dev/null @@ -1,6 +0,0 @@ -let OTA = { - // ## **`OTA.evdataOtaStatusMsg(evdata)`** - // Getter function for the `evdata` given to the event callback for the event - // `Event.OTA_STATUS`, see `Event.addHandler()` in `api_events.js`. - evdataOtaStatusMsg: ffi('char *mgos_ota_status_get_msg(void *)'), -}; diff --git a/libs/ota-http-client/mos.yml b/libs/ota-http-client/mos.yml deleted file mode 100644 index a601b6f..0000000 --- a/libs/ota-http-client/mos.yml +++ /dev/null @@ -1,31 +0,0 @@ -author: mongoose-os -description: Implements Mongoose OS OTA HTTP client -type: lib -version: 1.0 - -sources: - - src - -includes: - - include - -libs: - - origin: https://github.com/mongoose-os-libs/ota-common - -config_schema: - - ["update.url", "s", {title : "Fetch updates form here"}] - - ["update.interval", "i", {title : "Check for updates this often"}] - - # Default CA bundle for updating from mongoose-os.com (and any other site that uses LetsEncrypt) and S3. - - ["update.ssl_ca_file", "s", "ca.pem", {title : "TLS CA file"}] - - ["update.ssl_client_cert_file", "s", {title: "TLS client cert file"}] - - ["update.ssl_server_name", "s", {title : "TLS Server Name"}] - -tags: - - c - - ota - - http - - rpc - - docs:net:OTA via HTTP GET - -manifest_version: 2017-09-29 diff --git a/libs/ota-http-client/src/mgos_ota_http_client.c b/libs/ota-http-client/src/mgos_ota_http_client.c deleted file mode 100644 index f3b18e2..0000000 --- a/libs/ota-http-client/src/mgos_ota_http_client.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (c) 2014-2018 Cesanta Software Limited - * All rights reserved - * - * Licensed under the Apache License, Version 2.0 (the ""License""); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an ""AS IS"" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mgos_ota_http_client.h" - -#include "common/cs_dbg.h" -#include "mgos_event.h" -#include "mgos_hal.h" -#include "mgos_mongoose.h" -#include "mgos_ro_vars.h" -#include "mgos_sys_config.h" -#include "mgos_timers.h" -#include "mgos_utils.h" - -static void mgos_ota_http_start_internal(struct update_context *ctx, - const char *url, bool restrict_url); - -static void fw_download_handler(struct mg_connection *c, int ev, void *p, - void *user_data) { - struct mbuf *io = &c->recv_mbuf; - struct update_context *ctx = (struct update_context *) user_data; - int res = 0; - struct mg_str *loc; - (void) p; - - switch (ev) { - case MG_EV_CONNECT: { - int result = *((int *) p); - if (result != 0) { - LOG(LL_ERROR, ("Connect error: %d", result)); - ctx->status_msg = "Failed to connect"; - ctx->result = -10; - } - break; - } - case MG_EV_RECV: { - if (ctx->zip_file_size == 0) { - LOG(LL_DEBUG, ("Looking for HTTP header")); - struct http_message hm; - int parsed = mg_parse_http(io->buf, io->len, &hm, 0); - if (parsed <= 0) { - return; - } - if (hm.resp_code != 200) { - if (hm.resp_code == 304) { - ctx->result = 1; - ctx->need_reboot = false; - ctx->status_msg = "Not Modified"; - updater_finish(ctx); - } else if ((hm.resp_code == 301 || hm.resp_code == 302) && - (loc = mg_get_http_header(&hm, "Location")) != NULL) { - /* NUL-terminate the URL. Every header must be followed by \r\n, - * so there is deifnitely space there. */ - ((char *) loc->p)[loc->len] = '\0'; - /* We were told to look elsewhere. Detach update context from this - * connection so that it doesn't get finalized when it's closed. */ - LOG(LL_INFO, ("Got redirect: [%s]", loc->p)); - /* Do not restrict redirected URLs, cause Github redirects */ - mgos_ota_http_start_internal(ctx, loc->p, false); - c->user_data = NULL; - } else { - ctx->result = -hm.resp_code; - ctx->need_reboot = false; - ctx->status_msg = "Invalid HTTP response code"; - LOG(LL_ERROR, ("%s: %d", ctx->status_msg, hm.resp_code)); - updater_finish(ctx); - } - c->flags |= MG_F_CLOSE_IMMEDIATELY; - return; - } - if (hm.resp_code == 200 && hm.body.len != 0) { - LOG(LL_DEBUG, ("HTTP header: file size: %d", (int) hm.body.len)); - if (hm.body.len == (size_t) ~0) { - ctx->status_msg = - "Invalid content-length, perhaps chunked-encoding"; - LOG(LL_ERROR, (ctx->status_msg)); - c->flags |= MG_F_CLOSE_IMMEDIATELY; - break; - } else { - ctx->zip_file_size = hm.body.len; - } - - mbuf_remove(io, parsed); - } - } - - if (io->len != 0) { - res = updater_process(ctx, io->buf, io->len); - mbuf_remove(io, io->len); - - if (res == 0) { - if (is_write_finished(ctx)) res = updater_finalize(ctx); - if (res == 0) { - /* Need more data, everything is OK */ - break; - } - } - - if (res < 0) { - /* Error */ - LOG(LL_ERROR, ("Update error: %d %s", ctx->result, ctx->status_msg)); - } - c->flags |= MG_F_CLOSE_IMMEDIATELY; - } - break; - } - case MG_EV_CLOSE: { - if (ctx == NULL) break; - - if (is_write_finished(ctx)) updater_finalize(ctx); - - if (!is_update_finished(ctx)) { - /* Update failed or connection was terminated by server */ - if (ctx->status_msg == NULL) ctx->status_msg = "Update failed"; - ctx->result = -5; - } else if (is_reboot_required(ctx)) { - LOG(LL_INFO, ("OTA finished, rebooting device")); - /* Give it 3 seconds to drain any possible pending RPC calls */ - mgos_system_restart_after(3000); - } - updater_finish(ctx); - updater_context_free(ctx); - c->user_data = NULL; - break; - } - } -} - -void mgos_ota_http_start(struct update_context *ctx, const char *url) { - mgos_ota_http_start_internal(ctx, url, true); -} - -static void mgos_ota_http_start_internal(struct update_context *ctx, - const char *url, bool restrict_url) { - LOG(LL_INFO, - ("Update URL: %s, ct: %d, isv? %d %d", url, ctx->fctx.commit_timeout, - ctx->ignore_same_version, restrict_url)); - -#if defined(MGOS_FREE_BUILD) - if (restrict_url) { - const char *allowed_url_prefixes[] = { - "https://github.com/mongoose-os-apps/", "https://mongoose-os.com/", - "http://mongoose-os.com/", "https://dash.mongoose-os.com/", - "http://dash.mongoose-os.com/", NULL, - }; - int i; - for (i = 0; allowed_url_prefixes[i] != NULL; i++) { - const char *s = allowed_url_prefixes[i]; - if (strncmp(s, url, strlen(s)) == 0) break; - } - if (allowed_url_prefixes[i] == NULL) { - ctx->result = -10; - ctx->need_reboot = false; - ctx->status_msg = - "Free version of OTA library can only perform OTA from " - "github.com/mongoose-os-apps/. For " - "commercial version, please contact " - "https://mongoose-os.com/contact.html"; - LOG(LL_ERROR, ("%s", ctx->status_msg)); - updater_finish(ctx); - updater_context_free(ctx); - return; - } - } -#endif - - struct mg_connect_opts opts; - memset(&opts, 0, sizeof(opts)); - -#if MG_ENABLE_SSL - if (strlen(url) > 8 && strncmp(url, "https://", 8) == 0) { - opts.ssl_server_name = mgos_sys_config_get_update_ssl_server_name(); - opts.ssl_ca_cert = mgos_sys_config_get_update_ssl_ca_file(); - opts.ssl_cert = mgos_sys_config_get_update_ssl_client_cert_file(); - } -#endif - - char ehb[150]; - char *extra_headers = ehb; - mg_asprintf( - &extra_headers, sizeof(ehb), - "Connection: close\r\n" - "X-MGOS-Device-ID: %s %s\r\n" - "X-MGOS-FW-Version: %s %s %s\r\n", - (mgos_sys_config_get_device_id() ? mgos_sys_config_get_device_id() : "-"), - mgos_sys_ro_vars_get_mac_address(), mgos_sys_ro_vars_get_arch(), - mgos_sys_ro_vars_get_fw_version(), mgos_sys_ro_vars_get_fw_id()); - - struct mg_connection *c = mg_connect_http_opt( - mgos_get_mgr(), fw_download_handler, ctx, opts, url, extra_headers, NULL); - - if (extra_headers != ehb) free(extra_headers); - - if (c == NULL) { - LOG(LL_ERROR, ("Failed to connect to %s", url)); - ctx->result = -11; - ctx->need_reboot = false; - ctx->status_msg = "Failed to connect"; - updater_finish(ctx); - updater_context_free(ctx); - return; - } - - ctx->nc = c; -} - -static void mgos_ota_timer_cb(void *arg) { - if (mgos_sys_config_get_update_url() == NULL) return; - struct update_context *ctx = updater_context_create(0); - if (ctx == NULL) return; - ctx->ignore_same_version = true; - ctx->fctx.commit_timeout = mgos_sys_config_get_update_commit_timeout(); - mgos_ota_http_start(ctx, mgos_sys_config_get_update_url()); - - (void) arg; -} - -static void ota_request_cb(int ev, void *ev_data, void *userdata) { - struct ota_request_param *p = (struct ota_request_param *) ev_data; - mgos_ota_http_start(p->updater_context, p->location); - (void) ev; - (void) userdata; -} - -bool mgos_ota_http_client_init(void) { - const char *url = mgos_sys_config_get_update_url(); - int interval = mgos_sys_config_get_update_interval(); - if (url != NULL && interval > 0) { - LOG(LL_INFO, ("Updates from %s, every %d seconds", url, interval)); - mgos_set_timer(interval * 1000, MGOS_TIMER_REPEAT, mgos_ota_timer_cb, - (void *) url); - } - - const char *pref = mgos_sys_config_get_sys_pref_ota_lib(); - const char *me = "ota-http-client"; - bool ota_handler_set = false; - if ((pref == NULL || strcmp(pref, me) == 0) && - mgos_event_register_base(MGOS_EVENT_OTA_REQUEST, me)) { - mgos_event_add_handler(MGOS_EVENT_OTA_REQUEST, ota_request_cb, NULL); - ota_handler_set = true; - } - LOG(LL_INFO, ("Init done, ota_lib=%s, ota handler %d", - pref == NULL ? "(null)" : pref, ota_handler_set)); - (void) ota_handler_set; - return true; -} diff --git a/libs/rpc-service-ota/LICENSE b/libs/rpc-service-ota/LICENSE deleted file mode 100644 index 19bf2e3..0000000 --- a/libs/rpc-service-ota/LICENSE +++ /dev/null @@ -1,14 +0,0 @@ -Copyright (c) 2018 Cesanta Software Limited -All rights reserved - -Licensed under the Apache License, Version 2.0 (the ""License""); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an ""AS IS"" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/libs/rpc-service-ota/README.md b/libs/rpc-service-ota/README.md deleted file mode 100644 index 7b23e17..0000000 --- a/libs/rpc-service-ota/README.md +++ /dev/null @@ -1,89 +0,0 @@ -# RPC Service - OTA (Over The Air updates) - -This service provides an ability to manage OTA on devices remotely. -It is possible to call this service programmatically via serial, HTTP/RESTful, -Websocket, MQTT or other transports -(see [RPC section](/docs/mos/userguide/rpc.md)) or via the `mos` tool. - -See in-depth description of our OTA mechanism at -[Updating firmware reliably - embedded.com](http://www.embedded.com/design/prototyping-and-development/4443082/Updating-firmware-reliably). - -See OTA video tutorial: - - - -Below is a list of exported RPC methods and arguments: - -## OTA.Update -Trigger OTA firmware update. Arguments: -```javascript -{ - "url": "https://foo.com/fw123.zip", // Required. URL to the new firmware. - "commit_timeout": "300" // Optional. Time frame in seconds to do - // OTA.Commit after reboot. If commit is - // not done during the timeout, OTA rolls back. -} -``` - -A new firmware gets downloaded to the separate flash partition, -and is marked dirty. When the download is complete, device is rebooted. -After reboot, a firmware partition could become committed by calling -`OTA.Commit` - in which case, it is marked as "good". Otherwise, a device -reboots back into the old firmware after the `commit_timeout` seconds. -Example usage: - -
mos call OTA.Update '{"url": "http://1.2.3.4/fw.zip", "commit_timeout": 300}'
- - -## OTA.Commit -Commit current firmware. Arguments: none. - -Example usage: - -
mos call OTA.Commit
- - -## OTA.Revert -Rolls back to the previous firmware. Arguments: none. - -Example usage: -
mos call OTA.Revert
- - -## OTA.CreateSnapshot -Create new firmware patition with the copy of currently running firmware. Arguments: -```javascript -{ - // Optional. If true, then current firmware is uncommited, and needs to - // be explicitly commited after the first reboot. Otherwise, it'll reboot - // into the created snapshot. This option is useful if a dangerous, risky - // live update is to be done on the living device. Then, if the update - // fails and device bricks, it'll revert to the created good snapshot. - "set_as_revert": false, - // Optional. Same meaning as for OTA.Update - "commit_timeout": "300" -} -``` - -Example usage: -
mos call OTA.CreateSnapshot
- - -## OTA.GetBootState -Get current boot state. Arguments: none. - -Example usage: -
mos call OTA.GetBootState
-{
-  "active_slot": 0,       # Currently active flash partition.
-  "is_committed": true,   # Current firmware is marked as "good" (committed).
-  "revert_slot": 0,       # If uncommitted, slot to roll back to.
-  "commit_timeout": 0     # Commit timeout.
-}
- -## OTA.SetBootState -Get current boot state. Arguments: see `OTA.GetBootState` reply section. - -Example usage: -
mos call OTA.SetBootState '{"revert_slot": 1}'
diff --git a/libs/rpc-service-ota/include/mgos_rpc_service_ota.h b/libs/rpc-service-ota/include/mgos_rpc_service_ota.h deleted file mode 100644 index c8865ee..0000000 --- a/libs/rpc-service-ota/include/mgos_rpc_service_ota.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2014-2018 Cesanta Software Limited - * All rights reserved - * - * Licensed under the Apache License, Version 2.0 (the ""License""); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an ""AS IS"" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef CS_FW_SRC_MGOS_UPDATER_MG_RPC_H_ -#define CS_FW_SRC_MGOS_UPDATER_MG_RPC_H_ - -#include -#include -#include "common/mg_str.h" -#include "mgos_features.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -bool mgos_rpc_service_ota_init(void); -void mgos_updater_rpc_finish(int error_code, int64_t id, - const struct mg_str src); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* CS_FW_SRC_MGOS_UPDATER_MG_RPC_H_ */ diff --git a/libs/rpc-service-ota/mos.yml b/libs/rpc-service-ota/mos.yml deleted file mode 100644 index 192a249..0000000 --- a/libs/rpc-service-ota/mos.yml +++ /dev/null @@ -1,17 +0,0 @@ -author: mongoose-os -description: Support for Over-The-Air update via RPC -type: lib -version: 1.0 -manifest_version: 2017-09-29 -sources: - - src -includes: - - include -libs: - - origin: https://github.com/mongoose-os-libs/ota-http-client - - origin: https://github.com/mongoose-os-libs/rpc-common -tags: - - c - - ota - - rpc - - docs:rpc:Service - OTA diff --git a/libs/rpc-service-ota/src/mgos_rpc_service_ota.c b/libs/rpc-service-ota/src/mgos_rpc_service_ota.c deleted file mode 100644 index ab6492e..0000000 --- a/libs/rpc-service-ota/src/mgos_rpc_service_ota.c +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright (c) 2014-2018 Cesanta Software Limited - * All rights reserved - * - * Licensed under the Apache License, Version 2.0 (the ""License""); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an ""AS IS"" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mgos_rpc_service_ota.h" - -#include "mg_rpc.h" -#include "mgos_ota_http_client.h" -#include "mgos_rpc.h" - -#include "common/cs_dbg.h" -#include "common/mg_str.h" -#include "mgos_mongoose.h" -#include "mgos_updater_common.h" -#include "mgos_utils.h" - -static struct mg_rpc_request_info *s_update_req; - -static void ota_status_cb(int ev, void *ev_data, void *userdata) { - struct mgos_ota_status *s = (struct mgos_ota_status *) ev_data; - if (s_update_req == NULL) return; - if (s->state == MGOS_OTA_STATE_ERROR) { - mg_rpc_send_errorf(s_update_req, -1, s->msg); - s_update_req = NULL; - } else if (s->state == MGOS_OTA_STATE_SUCCESS) { - mg_rpc_send_responsef(s_update_req, "true"); - s_update_req = NULL; - } - (void) ev; - (void) userdata; -} - -static void handle_update_req(struct mg_rpc_request_info *ri, void *cb_arg, - struct mg_rpc_frame_info *fi, - struct mg_str args) { - char *blob_url = NULL; - struct json_token url_tok = JSON_INVALID_TOKEN; - int timeout = 0, commit_timeout = 0; - struct update_context *ctx = NULL; - - LOG(LL_DEBUG, ("Update request received: %.*s", (int) args.len, args.p)); - - const char *reply = "Malformed request"; - - if (args.len == 0) { - goto clean; - } - - json_scanf(args.p, args.len, ri->args_fmt, &url_tok, &timeout, - &commit_timeout); - - if (url_tok.len == 0 || url_tok.type != JSON_TYPE_STRING) goto clean; - - LOG(LL_DEBUG, ("URL: %.*s to: %d cto: %d", url_tok.len, url_tok.ptr, timeout, - commit_timeout)); - - /* - * If user setup callback for updater, just call it. - * User can start update with Sys.updater.start() - */ - - blob_url = calloc(1, url_tok.len + 1); - if (blob_url == NULL) { - LOG(LL_ERROR, ("Out of memory")); - return; - } - - memcpy(blob_url, url_tok.ptr, url_tok.len); - - ctx = updater_context_create(timeout); - if (ctx == NULL) { - reply = "Failed to init updater"; - goto clean; - } - ctx->fctx.commit_timeout = commit_timeout; - s_update_req = ri; - - mgos_ota_http_start(ctx, blob_url); - free(blob_url); - return; - -clean: - if (blob_url != NULL) free(blob_url); - LOG(LL_ERROR, ("Failed to start update: %s", reply)); - mg_rpc_send_errorf(ri, -1, reply); - ri = NULL; - (void) cb_arg; - (void) fi; -} - -static void handle_commit_req(struct mg_rpc_request_info *ri, void *cb_arg, - struct mg_rpc_frame_info *fi, - struct mg_str args) { - if (mgos_upd_commit()) { - mg_rpc_send_responsef(ri, NULL); - } else { - mg_rpc_send_errorf(ri, -1, NULL); - } - ri = NULL; - (void) cb_arg; - (void) fi; - (void) args; -} - -static void handle_revert_req(struct mg_rpc_request_info *ri, void *cb_arg, - struct mg_rpc_frame_info *fi, - struct mg_str args) { - if (mgos_upd_revert(false /* reboot */)) { - mg_rpc_send_responsef(ri, NULL); - mgos_system_restart_after(300); - } else { - mg_rpc_send_errorf(ri, -1, NULL); - } - ri = NULL; - (void) cb_arg; - (void) fi; - (void) args; -} - -static void handle_create_snapshot_req(struct mg_rpc_request_info *ri, - void *cb_arg, - struct mg_rpc_frame_info *fi, - struct mg_str args) { - const char *err_msg = NULL; - int ret = -1; - if (mgos_upd_is_committed()) { - ret = mgos_upd_create_snapshot(); - if (ret >= 0) { - bool set_as_revert = false; - int commit_timeout = -1; - json_scanf(args.p, args.len, ri->args_fmt, &set_as_revert, - &commit_timeout); - if (set_as_revert) { - struct mgos_upd_boot_state bs; - if (mgos_upd_boot_get_state(&bs)) { - bs.is_committed = false; - bs.revert_slot = ret; - if (mgos_upd_boot_set_state(&bs)) { - if (commit_timeout >= 0 && - !mgos_upd_set_commit_timeout(commit_timeout)) { - ret = -4; - err_msg = "Failed to set commit timeout"; - } - } else { - ret = -3; - err_msg = "Failed to set boot state"; - } - } else { - ret = -2; - err_msg = "Failed to get boot state"; - } - } - } else { - err_msg = "Failed to create snapshot"; - } - } else { - ret = -1; - err_msg = "Cannot create snapshots in uncommitted state"; - } - if (ret >= 0) { - mg_rpc_send_responsef(ri, "{slot: %d}", ret); - } else { - mg_rpc_send_errorf(ri, ret, err_msg); - } - (void) cb_arg; - (void) fi; -} - -static void handle_get_boot_state_req(struct mg_rpc_request_info *ri, - void *cb_arg, - struct mg_rpc_frame_info *fi, - struct mg_str args) { - struct mgos_upd_boot_state bs; - if (!mgos_upd_boot_get_state(&bs)) { - mg_rpc_send_errorf(ri, -1, NULL); - } else { - mg_rpc_send_responsef(ri, - "{active_slot: %d, is_committed: %B, revert_slot: " - "%d, commit_timeout: %d}", - bs.active_slot, bs.is_committed, bs.revert_slot, - mgos_upd_get_commit_timeout()); - } - (void) cb_arg; - (void) fi; - (void) args; -} - -static void handle_set_boot_state_req(struct mg_rpc_request_info *ri, - void *cb_arg, - struct mg_rpc_frame_info *fi, - struct mg_str args) { - int ret = 0; - struct mgos_upd_boot_state bs; - if (mgos_upd_boot_get_state(&bs)) { - int commit_timeout = -1; - if (json_scanf(args.p, args.len, ri->args_fmt, &bs.active_slot, - &bs.is_committed, &bs.revert_slot, &commit_timeout) > 0) { - ret = mgos_upd_boot_set_state(&bs) ? 0 : -3; - if (ret == 0 && commit_timeout >= 0) { - ret = mgos_upd_set_commit_timeout(commit_timeout) ? 0 : -4; - } - } else { - ret = -2; - } - } else { - ret = -1; - } - if (ret == 0) { - mg_rpc_send_responsef(ri, NULL); - } else { - mg_rpc_send_errorf(ri, ret, NULL); - } - (void) cb_arg; - (void) fi; -} - -static void handle_status(struct mg_rpc_request_info *ri, void *cb_arg, - struct mg_rpc_frame_info *fi, struct mg_str args) { - struct mgos_ota_status s; - mgos_upd_get_status(&s); - mg_rpc_send_responsef(ri, - "{state: %d, message: %Q, is_committed: %B, " - "progress_percent: %d, commit_timeout: %d, partition: " - "%d}", - s.state, s.msg, s.is_committed, s.progress_percent, - s.commit_timeout, s.partition); - (void) args; - (void) cb_arg; - (void) fi; -} - -static void handle_begin(struct mg_rpc_request_info *ri, void *cb_arg, - struct mg_rpc_frame_info *fi, struct mg_str args) { - int timeout = 0, commit_timeout = 0, size = 0; - json_scanf(args.p, args.len, ri->args_fmt, &timeout, &commit_timeout, &size); - struct update_context *ctx = updater_context_create(timeout); - if (ctx == NULL) { - mg_rpc_send_errorf(ri, -1, "Failed to init updater"); - } else { - ctx->fctx.commit_timeout = commit_timeout; - ctx->zip_file_size = size; - mg_rpc_send_responsef(ri, NULL); - } - (void) cb_arg; - (void) fi; -} - -static void handle_write(struct mg_rpc_request_info *ri, void *cb_arg, - struct mg_rpc_frame_info *fi, struct mg_str args) { - char *data = NULL; - int len; - struct update_context *ctx = updater_context_get_current(); - if (ctx == NULL) { - mg_rpc_send_errorf(ri, -1, "Update not started"); - return; - } - json_scanf(args.p, args.len, ri->args_fmt, &data, &len); - if (data == NULL) { - mg_rpc_send_errorf(ri, -1, "Data required"); - } else if (updater_process(ctx, data, len) < 0) { - mg_rpc_send_errorf(ri, -1, "Write error: %s", - (ctx->status_msg ? ctx->status_msg : "")); - } else { - mg_rpc_send_responsef(ri, NULL); - } - free(data); - (void) cb_arg; - (void) fi; -} - -static void handle_end(struct mg_rpc_request_info *ri, void *cb_arg, - struct mg_rpc_frame_info *fi, struct mg_str args) { - struct update_context *ctx = updater_context_get_current(); - if (ctx == NULL) { - mg_rpc_send_errorf(ri, -1, "Update not started"); - } else if (updater_finalize(ctx) < 0) { - mg_rpc_send_errorf(ri, -1, "Update finalize failed: %s", ctx->status_msg); - } else { - mgos_system_restart_after(500); - handle_status(ri, cb_arg, fi, args); - } - /* Finalized successfully or not, update is finished. */ - if (ctx != NULL) { - updater_finish(ctx); - updater_context_free(ctx); - } -} - -bool mgos_rpc_service_ota_init(void) { - struct mg_rpc *mg_rpc = mgos_rpc_get_global(); - if (mg_rpc == NULL) return true; - mg_rpc_add_handler(mg_rpc, "OTA.Update", - "{url: %T, timeout: %d, commit_timeout: %d}", - handle_update_req, NULL); - mg_rpc_add_handler(mg_rpc, "OTA.Commit", "", handle_commit_req, NULL); - mg_rpc_add_handler(mg_rpc, "OTA.Revert", "", handle_revert_req, NULL); - mg_rpc_add_handler(mg_rpc, "OTA.CreateSnapshot", - "{set_as_revert: %B, commit_timeout: %d}", - handle_create_snapshot_req, NULL); - mg_rpc_add_handler(mg_rpc, "OTA.Begin", - "{timeout: %d, commit_timeout: %d, size: %d}", - handle_begin, NULL); - mg_rpc_add_handler(mg_rpc, "OTA.Write", "{data: %V}", handle_write, NULL); - mg_rpc_add_handler(mg_rpc, "OTA.End", "", handle_end, NULL); - mg_rpc_add_handler(mg_rpc, "OTA.Status", "", handle_status, NULL); - mg_rpc_add_handler(mg_rpc, "OTA.GetBootState", "", handle_get_boot_state_req, - NULL); - mg_rpc_add_handler(mg_rpc, "OTA.SetBootState", - "{active_slot: %d, is_committed: %B, revert_slot: %d, " - "commit_timeout: %d}", - handle_set_boot_state_req, NULL); - - mgos_event_add_handler(MGOS_EVENT_OTA_STATUS, ota_status_cb, NULL); - - return true; -}