From 3d19ecebd19c433467c330e43f8bf8c9a624177c Mon Sep 17 00:00:00 2001 From: donghengqaz <930490596@qq.com> Date: Thu, 13 Feb 2020 16:55:04 +0800 Subject: [PATCH] feat(app_update): add app description data and its functions --- components/app_update/CMakeLists.txt | 4 +- components/app_update/Kconfig | 36 ++++++++ components/app_update/component.mk | 49 +++++++++++ components/app_update/esp_app_desc.c | 85 +++++++++++++++++++ components/app_update/esp_ota_ops.c | 22 +++++ components/app_update/include/esp_ota_ops.h | 25 ++++++ .../include/esp_app_format.h | 38 +++++++++ 7 files changed, 257 insertions(+), 2 deletions(-) create mode 100644 components/app_update/esp_app_desc.c create mode 100644 components/bootloader_support/include/esp_app_format.h diff --git a/components/app_update/CMakeLists.txt b/components/app_update/CMakeLists.txt index bc40d5b9..d9a55031 100644 --- a/components/app_update/CMakeLists.txt +++ b/components/app_update/CMakeLists.txt @@ -1,4 +1,4 @@ -idf_component_register(SRCS "esp_ota_ops.c" +idf_component_register(SRCS "esp_ota_ops.c" "esp_app_desc.c" INCLUDE_DIRS "include" REQUIRES spi_flash partition_table bootloader_support PRIV_REQUIRES util) @@ -14,7 +14,7 @@ string(SUBSTRING "${project_ver}" 0 31 PROJECT_VER_CUT) string(SUBSTRING "${project_name}" 0 31 PROJECT_NAME_CUT) set_source_files_properties( - SOURCE "esp_app_desc.c" + SOURCE "esp_app_desc.c" "esp_app_desc.c" PROPERTIES COMPILE_DEFINITIONS "PROJECT_VER=\"${PROJECT_VER_CUT}\"; PROJECT_NAME=\"${PROJECT_NAME_CUT}\"") diff --git a/components/app_update/Kconfig b/components/app_update/Kconfig index 67643b9f..aa9d138a 100644 --- a/components/app_update/Kconfig +++ b/components/app_update/Kconfig @@ -12,4 +12,40 @@ config APP_UPDATE_CHECK_APP_HASH help If enable this option, app update will check the hash of app binary data after downloading it. + config APP_COMPILE_TIME_DATE + bool "Use time/date stamp for app" + default y + help + If set, then the app will be built with the current time/date stamp. It is stored in the app description + structure. If not set, time/date stamp will be excluded from app image. This can be useful for getting the + same binary image files made from the same source, but at different times. + + config APP_EXCLUDE_PROJECT_VER_VAR + bool "Exclude PROJECT_VER from firmware image" + default n + help + The PROJECT_VER variable from the build system will not affect the firmware image. + This value will not be contained in the esp_app_desc structure. + + config APP_EXCLUDE_PROJECT_NAME_VAR + bool "Exclude PROJECT_NAME from firmware image" + default n + help + The PROJECT_NAME variable from the build system will not affect the firmware image. + This value will not be contained in the esp_app_desc structure. + + config APP_PROJECT_VER_FROM_CONFIG + bool "Get the project version from Kconfig" + default n + help + If this is enabled, then config item APP_PROJECT_VER will be used for the variable PROJECT_VER. + Other ways to set PROJECT_VER will be ignored. + + config APP_PROJECT_VER + string "Project version" + default "1" + depends on APP_PROJECT_VER_FROM_CONFIG + help + Project version + endmenu diff --git a/components/app_update/component.mk b/components/app_update/component.mk index c2c4c03a..14713393 100755 --- a/components/app_update/component.mk +++ b/components/app_update/component.mk @@ -3,3 +3,52 @@ # # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) +COMPONENT_ADD_LDFLAGS += -u esp_app_desc + +ifndef IS_BOOTLOADER_BUILD + # If ``CONFIG_APP_PROJECT_VER_FROM_CONFIG`` option is set, the value of ``CONFIG_APP_PROJECT_VER`` will be used + # Else, if ``PROJECT_VER`` variable set in project Makefile file, its value will be used. + # Else, if the ``$PROJECT_PATH/version.txt`` exists, its contents will be used as ``PROJECT_VER``. + # Else, if the project is located inside a Git repository, the output of git describe will be used. + # Otherwise, ``PROJECT_VER`` will be "1". + ifdef CONFIG_APP_PROJECT_VER_FROM_CONFIG + PROJECT_VER:= $(CONFIG_APP_PROJECT_VER) + else + ifneq ("${PROJECT_VER}", "") + PROJECT_VER:= $(PROJECT_VER) + else + ifneq ("$(wildcard ${PROJECT_PATH}/version.txt)","") + PROJECT_VER := $(shell cat ${PROJECT_PATH}/version.txt) + else + GIT_PROJECT_VER := $(shell cd ${PROJECT_PATH} && git describe --always --tags --dirty 2> /dev/null) + ifeq ("${GIT_PROJECT_VER}", "") + PROJECT_VER := "1" + $(info Project is not inside a git repository, or git repository has no commits) + $(info will not use 'git describe' to determine PROJECT_VER.) + else + PROJECT_VER:= $(GIT_PROJECT_VER) + endif # a git repository + endif # version.txt + endif # PROJECT_VER + endif # CONFIG_APP_PROJECT_VER_FROM_CONFIG + + # cut PROJECT_VER and PROJECT_NAME to required 32 characters. + PROJECT_VER_CUT := $(shell echo "$(PROJECT_VER)" | cut -c 1-31) + PROJECT_NAME_CUT := $(shell echo "$(PROJECT_NAME)" | cut -c 1-31) + + $(info App "$(PROJECT_NAME_CUT)" version: $(PROJECT_VER_CUT)) + + NEW_DEFINES:= "$(PROJECT_VER_CUT) $(PROJECT_NAME_CUT) $(IDF_VER)" + ifeq ("$(wildcard ${TMP_DEFINES})","") + OLD_DEFINES:= "" + else + OLD_DEFINES:= "$(shell cat $(TMP_DEFINES))" + endif + + # If NEW_DEFINES (PROJECT_VER, PROJECT_NAME) were changed then rebuild only esp_app_desc. + ifneq (${NEW_DEFINES}, ${OLD_DEFINES}) + $(shell echo $(NEW_DEFINES) > $(TMP_DEFINES); rm -f esp_app_desc.o;) + endif + + esp_app_desc.o: CPPFLAGS += -D PROJECT_VER=\""$(PROJECT_VER_CUT)"\" -D PROJECT_NAME=\""$(PROJECT_NAME_CUT)"\" +endif # IS_BOOTLOADER_BUILD diff --git a/components/app_update/esp_app_desc.c b/components/app_update/esp_app_desc.c new file mode 100644 index 00000000..299ae3ca --- /dev/null +++ b/components/app_update/esp_app_desc.c @@ -0,0 +1,85 @@ +// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD +// +// 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 "esp_ota_ops.h" +#include "esp_attr.h" +#include "sdkconfig.h" + +// Application version info +const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = { + .magic_word = ESP_APP_DESC_MAGIC_WORD, +#ifdef CONFIG_APP_EXCLUDE_PROJECT_VER_VAR + .version = "", +#else + .version = PROJECT_VER, +#endif + +#ifdef CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR + .project_name = "", +#else + .project_name = PROJECT_NAME, +#endif + .idf_ver = IDF_VER, + +#ifdef CONFIG_BOOTLOADER_APP_SECURE_VERSION + .secure_version = CONFIG_BOOTLOADER_APP_SECURE_VERSION, +#else + .secure_version = 0, +#endif + +#ifdef CONFIG_APP_COMPILE_TIME_DATE + .time = __TIME__, + .date = __DATE__, +#else + .time = "", + .date = "", +#endif +}; + + +#ifndef CONFIG_APP_EXCLUDE_PROJECT_VER_VAR +_Static_assert(sizeof(PROJECT_VER) <= sizeof(esp_app_desc.version), "PROJECT_VER is longer than version field in structure"); +#endif +_Static_assert(sizeof(IDF_VER) <= sizeof(esp_app_desc.idf_ver), "IDF_VER is longer than idf_ver field in structure"); +#ifndef CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR +_Static_assert(sizeof(PROJECT_NAME) <= sizeof(esp_app_desc.project_name), "PROJECT_NAME is longer than project_name field in structure"); +#endif + +const esp_app_desc_t *esp_ota_get_app_description(void) +{ + return &esp_app_desc; +} + +/* The following two functions may be called from the panic handler + * or core dump, hence IRAM_ATTR. + */ + +static inline char to_hex_digit(unsigned val) +{ + return (val < 10) ? ('0' + val) : ('a' + val - 10); +} + +int esp_ota_get_app_elf_sha256(char* dst, size_t size) +{ + size_t n = MIN((size - 1) / 2, sizeof(esp_app_desc.app_elf_sha256)); + const uint8_t* src = esp_app_desc.app_elf_sha256; + for (size_t i = 0; i < n; ++i) { + dst[2*i] = to_hex_digit(src[i] >> 4); + dst[2*i + 1] = to_hex_digit(src[i] & 0xf); + } + dst[2*n] = 0; + return 2*n + 1; +} diff --git a/components/app_update/esp_ota_ops.c b/components/app_update/esp_ota_ops.c index f0902c53..007fe795 100644 --- a/components/app_update/esp_ota_ops.c +++ b/components/app_update/esp_ota_ops.c @@ -637,3 +637,25 @@ const esp_partition_t* esp_ota_get_next_update_partition(const esp_partition_t * return default_ota; } + +esp_err_t esp_ota_get_partition_description(const esp_partition_t *partition, esp_app_desc_t *app_desc) +{ + if (partition == NULL || app_desc == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if(partition->type != ESP_PARTITION_TYPE_APP) { + return ESP_ERR_NOT_SUPPORTED; + } + + esp_err_t err = esp_partition_read(partition, sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t), app_desc, sizeof(esp_app_desc_t)); + if (err != ESP_OK) { + return err; + } + + if (app_desc->magic_word != ESP_APP_DESC_MAGIC_WORD) { + return ESP_ERR_NOT_FOUND; + } + + return ESP_OK; +} diff --git a/components/app_update/include/esp_ota_ops.h b/components/app_update/include/esp_ota_ops.h index 3d1155c3..dc9d1013 100755 --- a/components/app_update/include/esp_ota_ops.h +++ b/components/app_update/include/esp_ota_ops.h @@ -21,6 +21,7 @@ #include "esp_err.h" #include "esp_partition.h" #include "esp_spi_flash.h" +#include "esp_app_format.h" #ifdef __cplusplus extern "C" @@ -174,6 +175,30 @@ const esp_partition_t* esp_ota_get_running_partition(void); */ const esp_partition_t* esp_ota_get_next_update_partition(const esp_partition_t *start_from); +/** + * @brief Return esp_app_desc structure. This structure includes app version. + * + * Return description for running app. + * @return Pointer to esp_app_desc structure. + */ +const esp_app_desc_t *esp_ota_get_app_description(void); + +/** + * @brief Returns esp_app_desc structure for app partition. This structure includes app version. + * + * Returns a description for the requested app partition. + * @param[in] partition Pointer to app partition. (only app partition) + * @param[out] app_desc Structure of info about app. + * @return + * - ESP_OK Successful. + * - ESP_ERR_NOT_FOUND app_desc structure is not found. Magic word is incorrect. + * - ESP_ERR_NOT_SUPPORTED Partition is not application. + * - ESP_ERR_INVALID_ARG Arguments is NULL or if partition's offset exceeds partition size. + * - ESP_ERR_INVALID_SIZE Read would go out of bounds of the partition. + * - or one of error codes from lower-level flash driver. + */ +esp_err_t esp_ota_get_partition_description(const esp_partition_t *partition, esp_app_desc_t *app_desc); + #ifdef __cplusplus } #endif diff --git a/components/bootloader_support/include/esp_app_format.h b/components/bootloader_support/include/esp_app_format.h new file mode 100644 index 00000000..a21ad60e --- /dev/null +++ b/components/bootloader_support/include/esp_app_format.h @@ -0,0 +1,38 @@ +// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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 + +#define ESP_APP_DESC_MAGIC_WORD 0xABCD5432 /*!< The magic word for the esp_app_desc structure that is in DROM. */ + +/** + * @brief Description about application. + */ +typedef struct { + uint32_t magic_word; /*!< Magic word ESP_APP_DESC_MAGIC_WORD */ + uint32_t secure_version; /*!< Secure version */ + uint32_t reserv1[2]; /*!< reserv1 */ + char version[32]; /*!< Application version */ + char project_name[32]; /*!< Project name */ + char time[16]; /*!< Compile time */ + char date[16]; /*!< Compile date*/ + char idf_ver[32]; /*!< Version IDF */ + uint8_t app_elf_sha256[32]; /*!< sha256 of elf file */ + uint32_t reserv2[20]; /*!< reserv2 */ +} esp_app_desc_t; + +/** @cond */ +_Static_assert(sizeof(esp_app_desc_t) == 256, "esp_app_desc_t should be 256 bytes"); +/** @endcond */