ota.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. #include "ota.h"
  2. #include "esp_app_format.h"
  3. #include "esp_heap_caps.h"
  4. #include "esp_log.h"
  5. #include "esp_ota_ops.h"
  6. #include "mbedtls/sha256.h"
  7. #include <string.h>
  8. #include <sys/param.h>
  9. static const char *TAG = "ota";
  10. /**
  11. * Validate an in-memory firmware image before writing to flash.
  12. * Checks: minimum size, magic byte, segment count, and SHA-256 digest
  13. * (when the image indicates a hash is appended).
  14. */
  15. static esp_err_t ota_validate_image(const uint8_t *image, size_t len) {
  16. if (len < sizeof(esp_image_header_t)) {
  17. ESP_LOGE(TAG, "Image too small (%zu bytes)", len);
  18. return ESP_ERR_INVALID_SIZE;
  19. }
  20. const esp_image_header_t *header = (const esp_image_header_t *)image;
  21. if (header->magic != ESP_IMAGE_HEADER_MAGIC) {
  22. ESP_LOGE(TAG, "Bad image magic: 0x%02x (expected 0x%02x)", header->magic,
  23. ESP_IMAGE_HEADER_MAGIC);
  24. return ESP_ERR_INVALID_STATE;
  25. }
  26. if (header->segment_count == 0 ||
  27. header->segment_count > ESP_IMAGE_MAX_SEGMENTS) {
  28. ESP_LOGE(TAG, "Bad segment count: %u", header->segment_count);
  29. return ESP_ERR_INVALID_STATE;
  30. }
  31. if (header->hash_appended) {
  32. if (len < 32) {
  33. ESP_LOGE(TAG, "Image claims hash but is too small");
  34. return ESP_ERR_INVALID_SIZE;
  35. }
  36. // SHA-256 digest covers everything except the last 32 bytes
  37. size_t data_len = len - 32;
  38. const uint8_t *expected_hash = image + data_len;
  39. uint8_t computed_hash[32];
  40. mbedtls_sha256(image, data_len, computed_hash, 0);
  41. if (memcmp(computed_hash, expected_hash, 32) != 0) {
  42. ESP_LOGE(TAG, "SHA-256 mismatch — firmware is corrupt");
  43. return ESP_ERR_INVALID_STATE;
  44. }
  45. ESP_LOGI(TAG, "SHA-256 verified OK");
  46. }
  47. return ESP_OK;
  48. }
  49. /**
  50. * Receive firmware into a PSRAM buffer, validate, then write to flash.
  51. * Returns ESP_ERR_NO_MEM if PSRAM allocation fails (caller can fall back).
  52. */
  53. static esp_err_t ota_buffered(httpd_req_t *req) {
  54. size_t fw_size = req->content_len;
  55. uint8_t *fw_buf = heap_caps_malloc(fw_size, MALLOC_CAP_SPIRAM);
  56. if (!fw_buf) {
  57. ESP_LOGW(TAG, "Cannot allocate %zu bytes in PSRAM", fw_size);
  58. return ESP_ERR_NO_MEM;
  59. }
  60. // Receive entire firmware into RAM
  61. ESP_LOGI(TAG, "Receiving firmware into PSRAM (%zu bytes)...", fw_size);
  62. size_t received = 0;
  63. while (received < fw_size) {
  64. int recv_len =
  65. httpd_req_recv(req, (char *)fw_buf + received, fw_size - received);
  66. if (recv_len == HTTPD_SOCK_ERR_TIMEOUT) {
  67. continue;
  68. } else if (recv_len <= 0) {
  69. ESP_LOGE(TAG, "Receive error: %d", recv_len);
  70. heap_caps_free(fw_buf);
  71. return ESP_FAIL;
  72. }
  73. received += recv_len;
  74. }
  75. // Validate before touching flash
  76. esp_err_t err = ota_validate_image(fw_buf, fw_size);
  77. if (err != ESP_OK) {
  78. heap_caps_free(fw_buf);
  79. return err;
  80. }
  81. // Write validated image to flash
  82. const esp_partition_t *ota_partition =
  83. esp_ota_get_next_update_partition(NULL);
  84. if (!ota_partition) {
  85. ESP_LOGE(TAG, "No OTA partition found");
  86. heap_caps_free(fw_buf);
  87. return ESP_ERR_NOT_FOUND;
  88. }
  89. esp_ota_handle_t ota_handle;
  90. err = esp_ota_begin(ota_partition, fw_size, &ota_handle);
  91. if (err != ESP_OK) {
  92. ESP_LOGE(TAG, "esp_ota_begin failed: %s", esp_err_to_name(err));
  93. heap_caps_free(fw_buf);
  94. return err;
  95. }
  96. // Write in 4 KB chunks to avoid watchdog triggers on large images
  97. size_t offset = 0;
  98. while (offset < fw_size) {
  99. size_t chunk = MIN(fw_size - offset, 4096);
  100. if (esp_ota_write(ota_handle, fw_buf + offset, chunk) != ESP_OK) {
  101. ESP_LOGE(TAG, "Flash write failed at offset %zu", offset);
  102. esp_ota_abort(ota_handle);
  103. heap_caps_free(fw_buf);
  104. return ESP_FAIL;
  105. }
  106. offset += chunk;
  107. }
  108. heap_caps_free(fw_buf);
  109. if (esp_ota_end(ota_handle) != ESP_OK) {
  110. ESP_LOGE(TAG, "Image validation failed (esp_ota_end)");
  111. return ESP_FAIL;
  112. }
  113. if (esp_ota_set_boot_partition(ota_partition) != ESP_OK) {
  114. ESP_LOGE(TAG, "Failed to set boot partition");
  115. return ESP_FAIL;
  116. }
  117. ESP_LOGI(TAG, "OTA update successful (buffered)");
  118. return ESP_OK;
  119. }
  120. /**
  121. * Stream firmware directly to flash (original approach, used as fallback).
  122. */
  123. static esp_err_t ota_streaming(httpd_req_t *req) {
  124. const esp_partition_t *ota_partition =
  125. esp_ota_get_next_update_partition(NULL);
  126. if (!ota_partition) {
  127. ESP_LOGE(TAG, "No OTA partition found");
  128. return ESP_ERR_NOT_FOUND;
  129. }
  130. esp_ota_handle_t ota_handle;
  131. esp_err_t err = esp_ota_begin(ota_partition, OTA_SIZE_UNKNOWN, &ota_handle);
  132. if (err != ESP_OK) {
  133. ESP_LOGE(TAG, "esp_ota_begin failed: %s", esp_err_to_name(err));
  134. return err;
  135. }
  136. char buf[1024];
  137. size_t remaining = req->content_len;
  138. ESP_LOGI(TAG, "Receiving firmware via streaming (%zu bytes)...", remaining);
  139. while (remaining > 0) {
  140. int recv_len = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)));
  141. if (recv_len == HTTPD_SOCK_ERR_TIMEOUT) {
  142. continue;
  143. } else if (recv_len <= 0) {
  144. ESP_LOGE(TAG, "Receive error: %d", recv_len);
  145. esp_ota_abort(ota_handle);
  146. return ESP_FAIL;
  147. }
  148. if (esp_ota_write(ota_handle, buf, recv_len) != ESP_OK) {
  149. ESP_LOGE(TAG, "Flash write failed");
  150. esp_ota_abort(ota_handle);
  151. return ESP_FAIL;
  152. }
  153. remaining -= recv_len;
  154. }
  155. if (esp_ota_end(ota_handle) != ESP_OK) {
  156. ESP_LOGE(TAG, "Image validation failed");
  157. return ESP_FAIL;
  158. }
  159. if (esp_ota_set_boot_partition(ota_partition) != ESP_OK) {
  160. ESP_LOGE(TAG, "Failed to set boot partition");
  161. return ESP_FAIL;
  162. }
  163. ESP_LOGI(TAG, "OTA update successful (streaming)");
  164. return ESP_OK;
  165. }
  166. esp_err_t ota_start_from_http(httpd_req_t *req) {
  167. // Try RAM-buffered OTA first (validates image before writing to flash).
  168. // Falls back to streaming if PSRAM is not available or too small.
  169. esp_err_t err = ota_buffered(req);
  170. if (err == ESP_ERR_NO_MEM) {
  171. ESP_LOGW(TAG, "Falling back to streaming OTA (no PSRAM available)");
  172. err = ota_streaming(req);
  173. }
  174. return err;
  175. }