ESP-IDF: формат файла образа приложения |
![]() |
Добавил(а) microsin |
Двоичный образ приложения, который записывается в раздел OTA [2], состоит из следующих структур: 1. esp_image_header_t: структура, описывающая режим SPI flash и количество сегментов памяти. Структура esp_image_header_t содержит 24 байта, и находится в самом начале (со смещением 0) файла образа. Пример содержимого структуры в файле образа приложения (дамп 24 первых байт двоичного образа прошивки): E9 - магическое число, маркер начала структуры esp_image_header_t. 07 - 7 сегментов памяти. 02 - режим программирования DIO для SPI flash (02 соответствует ESP_IMAGE_SPI_MODE_DIO из перечисления esp_image_spi_mode_t). 22 - скорость SPI flash 20 МГц (2 соответствует ESP_IMAGE_SPI_SPEED_20M из перечисления esp_image_spi_freq_t), размер SPI flash 4 мегабайта (2 соответствует ESP_IMAGE_FLASH_SIZE_4MB из перечисления esp_image_flash_size_t). 02 04 38 40 - адрес входа в приложение 0x40380402. EE - означает, что режим защиты памяти flash от записи запрещен. 00 00 00 - нагрузочная способность ножек обмена с памятью SPI. 05 00 - приложение для чипа ESP32-C3 (0x0005 соответствует ESP_CHIP_ID_ESP32C3 из перечисления esp_chip_id_t). 03 - минимально допустимая ревизия чипа. 00 00 00 00 00 00 00 00 - зарезервированные байты. 01 - после контрольной суммы добавлена цифровая подпись SHA256. 2. esp_image_segment_header_t: структура, описывающая каждый сегмент, его длину и размещение в памяти ESP32, за которой идут данные длиной data_len. За структурой esp_image_header_t непосредственно следует структура esp_image_segment_header_t, состоящая из 8 байт, и за ней идут данные сегмента. Пример расшифровки дампа первого сегмента приложения (дамп первых 320 байт двоичного файла прошивки приложения): 20 00 10 3С - адрес загрузки первого сегмента 0x3C100020. 38 63 06 00 - размер первого сегмента 0x00066338 байт. Далее идут байты 32 54 CD AB, по ним мы узнаем магическое число маркера ESP_APP_DESC_MAGIC_WORD (==0xABCD5432) начала структуры esp_app_desc_t с описанием приложения: 32 54 CD AB - константа 0xABCD5432 (ESP_APP_DESC_MAGIC_WORD). 00 00 00 00 - Secure version. 00 00 00 00 00 00 00 00 - два зарезервированных 32-битных слова, uint32_t reserv1[2]. 66 61 62 33 39 37 32 00 00 00 00 00 00 00 00 00 D0 90 D0 BB D0 B8 D1 81 D0 B0 0A 00 00 00 00 00 31 32 3A 34 35 3A 31 31 00 00 00 00 00 00 00 00 - время компиляции "12:45:11". 41 70 72 20 20 33 20 32 30 32 33 00 00 00 00 00 - дата компиляции "Apr 3 2023". 76 34 2E 34 2E 31 2D 64 69 72 74 79 00 00 00 00 F7 65 9B 98 0D B9 F4 75 B9 43 CD 66 A9 D1 C0 54 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Структура esp_image_header_t: /**
* @brief Основной заголовок двоичного образа
*/
typedef struct { uint8_t magic; /*!< Магическая константа ESP_IMAGE_HEADER_MAGIC (0xE9) */ uint8_t segment_count; /*!< Количество сегментов памяти */ uint8_t spi_mode; /*!< Режим чтения flash (esp_image_spi_mode_t как uint8_t) */ uint8_t spi_speed: 4; /*!< Частота flash (esp_image_spi_freq_t как uint8_t) */ uint8_t spi_size: 4; /*!< Размер микросхемы flash (esp_image_flash_size_t как uint8_t) */ uint32_t entry_addr; /*!< Адрес входа */ uint8_t wp_pin; /*!< Ножка WP, когда выводы SPI установлены через efuse (считываются из ROM bootloader, IDF bootloader использует программное обеспечение для конфигурирования вывода WP,
и устанавливает это поле в 0xEE=disabled) */ uint8_t spi_pin_drv[3]; /*!< Нагрузочная способность выводов SPI flash (считывается из ROM bootloader) */ esp_chip_id_t chip_id; /*!< Идентификатор чипа */ uint8_t min_chip_rev; /*!< Минимальная ревизия чипа, поддерживаемая образом(1) */ uint8_t reserved[8]; /*!< Зарезервированные байты в заголовке, в настоящее время не используются(2) */ uint8_t hash_appended; /*!< Если 1, то цифровая подпись SHA256 "simple hash" (всего образа) добавлена после контрольной суммы. Подпись включена в длину образа. Эта подпись
отдельная для secure boot, и используется только для проверки целостности образа. Для подписанных образов secure boot эта подпись добавляется после
этого (и simple hash добавлены в подписанные данные). */ } __attribute__((packed)) esp_image_header_t; Примечания: (1) Начиная с момента, когда Major и Minor ревизии чипа были представлены в eFuse чипа, это поле больше не используется. Однако из соображений совместимости разработчики оставили это поле и данные в нем. Вместо этого поля используйте min_chip_rev_full. ПО интерпретирует это поле как Major-версия чипов, и как Minor-версию для ESP32-C3. Идентификаторы чипа, сохраняемые в поле chip_id: /**
* @brief ESP chip ID
*/
typedef enum { ESP_CHIP_ID_ESP32 = 0x0000, /*!< ESP32 */ ESP_CHIP_ID_ESP32S2 = 0x0002, /*!< ESP32-S2 */ ESP_CHIP_ID_ESP32C3 = 0x0005, /*!< ESP32-C3 */ ESP_CHIP_ID_ESP32S3 = 0x0009, /*!< ESP32-S3 */ ESP_CHIP_ID_ESP32H2 = 0x000A, /*!< ESP32-H2 */ // ESP32H2-TODO: IDF-3475 ESP_CHIP_ID_INVALID = 0xFFFF /*!< Недопустимый chip ID (это значение было определено для гарантии, что у esp_chip_id_t размер всегда будет равен 2 байтам) */ } __attribute__((packed)) esp_chip_id_t; Структура esp_image_segment_header_t: /**
* @brief Заголовок сегмента двоичного образа
*/
typedef struct { uint32_t load_addr; /*!< Адрес сегмента */ uint32_t data_len; /*!< Длина данных сегмента */ } esp_image_segment_header_t; Смещение данных для каждого сегмента в образе вычисляется следующим образом: Смещение для сегмента 0 = sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t). Количество сегментов определено в поле segment_count структуры esp_image_header_t. Оно не может быть больше константы ESP_IMAGE_MAX_SEGMENTS (в настоящее время 16). Чтобы получить список сегментов вашего образа, запустите следующую команду: $ esptool.py --chip esp32 image_info build/app.bin esptool.py v2.3.1 Image version: 1 Entry point: 40080ea4 13 segments Segment 1: len 0x13ce0 load 0x3f400020 file_offs 0x00000018 SOC_DROM Segment 2: len 0x00000 load 0x3ff80000 file_offs 0x00013d00 SOC_RTC_DRAM Segment 3: len 0x00000 load 0x3ff80000 file_offs 0x00013d08 SOC_RTC_DRAM Segment 4: len 0x028e0 load 0x3ffb0000 file_offs 0x00013d10 DRAM Segment 5: len 0x00000 load 0x3ffb28e0 file_offs 0x000165f8 DRAM Segment 6: len 0x00400 load 0x40080000 file_offs 0x00016600 SOC_IRAM Segment 7: len 0x09600 load 0x40080400 file_offs 0x00016a08 SOC_IRAM Segment 8: len 0x62e4c load 0x400d0018 file_offs 0x00020010 SOC_IROM Segment 9: len 0x06cec load 0x40089a00 file_offs 0x00082e64 SOC_IROM Segment 10: len 0x00000 load 0x400c0000 file_offs 0x00089b58 SOC_RTC_IRAM Segment 11: len 0x00004 load 0x50000000 file_offs 0x00089b60 SOC_RTC_DATA Segment 12: len 0x00000 load 0x50000004 file_offs 0x00089b6c SOC_RTC_DATA Segment 13: len 0x00000 load 0x50000004 file_offs 0x00089b74 SOC_RTC_DATA Checksum: e8 (valid) Validation Hash: 407089ca0eae2bbf83b4120979d3354b1c938a49cb7a0c997f240474ef2ec76b (valid) Вы также можете увидеть информацию о сегментах в логе ESP-IDF, когда загружается ваше приложение: Для дополнительной информации о типах сегментов памяти и их диапазонах адресов см. руководство по используемому чипу (например, для ESP32-C3 это PDF-документ "ESP32-C3 Technical Reference Manual", раздел System and Memory -> Embedded Memory). 3. В образе есть один байт контрольной суммы, который находится после последнего сегмента. Этот байт записывается на границу памяти, выровненную на 16 байт, поэтому образу приложения может понадобиться дополнение. 4. Если установлено в 1 поле hash_appended структуры esp_image_header_t, то добавляется контрольная сумма SHA256. Значение хэша SHA256 вычисляется по диапазону от первого байта и данных SHA256. Длина данных SHA256 составляет 32 байта. 5. Если опция CONFIG_SECURE_SIGNED_APPS_SCHEME установлена в ECDSA, то у образа имеется 68 дополнительных байт для сигнатуры ECDSA, которые включают version word (4 байта) и signature data (64 байта). 6. Если опция CONFIG_SECURE_SIGNED_APPS_SCHEME установлена в RSA или ECDSA (V2), то у образа приложения будет дополнительный сектор сигнатуры размером 4 килобайта. Для дополнительной информации по этому сектору сигнатуры см. документацию [3]. [Описание приложения] Сегмент DROM бинарника приложения начинается со структуры esp_app_desc_t, которая содержит специальные поля, описывающие приложение: /**
* @brief Описание приложения.
*/
typedef struct { uint32_t magic_word; /*!< Магическое слово ESP_APP_DESC_MAGIC_WORD (==0xABCD5432) */ uint32_t secure_version; /*!< Secure version */ uint32_t reserv1[2]; /*!< reserv1 */ char version[32]; /*!< Версия приложения */ char project_name[32]; /*!< Имя проекта */ char time[16]; /*!< Время компиляции */ char date[16]; /*!< Дата компиляции */ char idf_ver[32]; /*!< Версия IDF */ uint8_t app_elf_sha256[32]; /*!< sha256 elf-файла */ uint32_t reserv2[20]; /*!< reserv2 */ } esp_app_desc_t; magic_word - 32-битная константа, маркер начала структуры описания приложения. Примечание (*): ASCIIZ-строка, максимальная длина которой 32 символа, включая null-символ терминатора. Например, если длина PROJECT_NAME превышает 31 символ, то лишние символы отбрасываются. Эта структура полезна для идентификации образов, загруженных по радиоканалу (Over-the-Air, OTA [4]), и у неё фиксированное смещение = sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t). Как только устройство примет первый фрагмент образа, содержащий эту структуру, у него уже будет вся информация об образе приложения, на основе которой может быть принято решение, должно ли обновление продолжиться, или нет. Чтобы получить структуру esp_app_desc_t для текущего работающего приложения, используйте esp_app_get_description(). Чтобы получить структуру esp_app_desc_t для другого раздела OTA, используйте esp_ota_get_partition_description(). [Добавление пользовательской структуры в приложение] Пользователи также имеют возможность применить аналогичную структуру с фиксированным смещением относительно начала образа. Для добавления пользовательской структуры к образу можно использовать следующий шаблон: const __attribute__((section(".rodata_custom_desc"))) esp_custom_app_desc_t custom_app_desc = { ... } Смещение для пользовательской структуры будет равно sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t). Для гарантии, что пользовательская структура находится в образе, даже если она не используется, необходимо в файл CMakeLists.txt добавить target_link_libraries(${COMPONENT_TARGET} "-u custom_app_desc"). В заголовочном файле components/bootloader_support/include/esp_app_format.h описаны все типы данных, используемые для определения формата образа приложения. Для дополнительной информации см. документацию [1]. [Ссылки] 1. ESP32-C3 App Image Format site:espressif.com. |