partition.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. /*
  2. * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdlib.h>
  7. #include <assert.h>
  8. #include <string.h>
  9. #include <stdio.h>
  10. #include <sys/lock.h>
  11. #include "sdkconfig.h"
  12. #include "esp_flash_partitions.h"
  13. #include "esp_attr.h"
  14. #include "esp_flash.h"
  15. #include "esp_partition.h"
  16. #if !CONFIG_IDF_TARGET_LINUX
  17. #include "esp_flash_encrypt.h"
  18. #endif
  19. #include "esp_log.h"
  20. #include "esp_rom_md5.h"
  21. #include "bootloader_util.h"
  22. #if CONFIG_IDF_TARGET_LINUX
  23. #if __has_include(<bsd/string.h>)
  24. #include <bsd/string.h>
  25. #endif
  26. #include "esp_private/partition_linux.h"
  27. #endif
  28. #ifndef CONFIG_IDF_TARGET_LINUX
  29. #define MMU_PAGE_SIZE CONFIG_MMU_PAGE_SIZE
  30. #else
  31. // No relation to the page size on Linux; assume the same value as on ESP32
  32. #define MMU_PAGE_SIZE 65536
  33. #endif // CONFIG_MMU_PAGE_SIZE
  34. #ifndef NDEBUG
  35. // Enable built-in checks in queue.h in debug builds
  36. #define INVARIANTS
  37. #endif
  38. #include "sys/queue.h"
  39. typedef struct partition_list_item_ {
  40. esp_partition_t info;
  41. bool user_registered;
  42. SLIST_ENTRY(partition_list_item_) next;
  43. } partition_list_item_t;
  44. typedef struct esp_partition_iterator_opaque_ {
  45. esp_partition_type_t type; // requested type
  46. esp_partition_subtype_t subtype; // requested subtype
  47. const char *label; // requested label (can be NULL)
  48. partition_list_item_t *next_item; // next item to iterate to
  49. esp_partition_t *info; // pointer to info (it is redundant, but makes code more readable)
  50. } esp_partition_iterator_opaque_t;
  51. static SLIST_HEAD(partition_list_head_, partition_list_item_) s_partition_list = SLIST_HEAD_INITIALIZER(s_partition_list);
  52. static _lock_t s_partition_list_lock;
  53. static const char *TAG = "partition";
  54. // Create linked list of partition_list_item_t structures.
  55. // This function is called only once, with s_partition_list_lock taken.
  56. static esp_err_t load_partitions(void)
  57. {
  58. const uint8_t *p_start;
  59. const uint8_t *p_end;
  60. #if !CONFIG_IDF_TARGET_LINUX
  61. spi_flash_mmap_handle_t handle;
  62. #endif
  63. // Temporary list of loaded partitions, if valid then we copy this to s_partition_list
  64. typeof(s_partition_list) new_partitions_list = SLIST_HEAD_INITIALIZER(s_partition_list);
  65. partition_list_item_t *last = NULL;
  66. #if CONFIG_PARTITION_TABLE_MD5
  67. const uint8_t *md5_part = NULL;
  68. const uint8_t *stored_md5;
  69. uint8_t calc_md5[ESP_ROM_MD5_DIGEST_LEN];
  70. md5_context_t context;
  71. esp_rom_md5_init(&context);
  72. #endif
  73. uint32_t partition_align_pg_size = (ESP_PARTITION_TABLE_OFFSET) & ~(MMU_PAGE_SIZE - 1);
  74. uint32_t partition_pad = ESP_PARTITION_TABLE_OFFSET - partition_align_pg_size;
  75. #if CONFIG_IDF_TARGET_LINUX
  76. esp_err_t err = esp_partition_file_mmap(&p_start);
  77. #else
  78. esp_err_t err = spi_flash_mmap(partition_align_pg_size,
  79. SPI_FLASH_SEC_SIZE, SPI_FLASH_MMAP_DATA, (const void **)&p_start, &handle);
  80. #endif
  81. if (err != ESP_OK) {
  82. return err;
  83. }
  84. // calculate partition address within mmap-ed region
  85. p_start += partition_pad;
  86. p_end = p_start + SPI_FLASH_SEC_SIZE;
  87. for (const uint8_t *p_entry = p_start; p_entry < p_end; p_entry += sizeof(esp_partition_info_t)) {
  88. esp_partition_info_t entry;
  89. // copying to RAM instead of using pointer to flash to avoid any chance of TOCTOU due to cache miss
  90. // when flash encryption is used
  91. memcpy(&entry, p_entry, sizeof(entry));
  92. #if CONFIG_PARTITION_TABLE_MD5
  93. if (entry.magic == ESP_PARTITION_MAGIC_MD5) {
  94. md5_part = p_entry;
  95. break;
  96. }
  97. #endif
  98. if (entry.magic != ESP_PARTITION_MAGIC) {
  99. break;
  100. }
  101. #if CONFIG_PARTITION_TABLE_MD5
  102. esp_rom_md5_update(&context, &entry, sizeof(entry));
  103. #endif
  104. // allocate new linked list item and populate it with data from partition table
  105. partition_list_item_t *item = (partition_list_item_t *) calloc(sizeof(partition_list_item_t), 1);
  106. if (item == NULL) {
  107. err = ESP_ERR_NO_MEM;
  108. break;
  109. }
  110. #if CONFIG_IDF_TARGET_LINUX
  111. item->info.flash_chip = NULL;
  112. #else
  113. item->info.flash_chip = esp_flash_default_chip;
  114. #endif
  115. item->info.address = entry.pos.offset;
  116. item->info.size = entry.pos.size;
  117. item->info.type = entry.type;
  118. item->info.subtype = entry.subtype;
  119. item->info.encrypted = entry.flags & PART_FLAG_ENCRYPTED;
  120. item->user_registered = false;
  121. #if CONFIG_IDF_TARGET_LINUX
  122. item->info.encrypted = false;
  123. #else
  124. if (!esp_flash_encryption_enabled()) {
  125. /* If flash encryption is not turned on, no partitions should be treated as encrypted */
  126. item->info.encrypted = false;
  127. } else if (entry.type == ESP_PARTITION_TYPE_APP
  128. || (entry.type == ESP_PARTITION_TYPE_DATA && entry.subtype == ESP_PARTITION_SUBTYPE_DATA_OTA)
  129. || (entry.type == ESP_PARTITION_TYPE_DATA && entry.subtype == ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS)) {
  130. /* If encryption is turned on, all app partitions and OTA data
  131. are always encrypted */
  132. item->info.encrypted = true;
  133. }
  134. #endif
  135. #if CONFIG_NVS_COMPATIBLE_PRE_V4_3_ENCRYPTION_FLAG
  136. if (entry.type == ESP_PARTITION_TYPE_DATA &&
  137. entry.subtype == ESP_PARTITION_SUBTYPE_DATA_NVS &&
  138. (entry.flags & PART_FLAG_ENCRYPTED)) {
  139. ESP_LOGI(TAG, "Ignoring encrypted flag for \"%s\" partition", entry.label);
  140. item->info.encrypted = false;
  141. }
  142. #endif
  143. // item->info.label is initialized by calloc, so resulting string will be null terminated
  144. strncpy(item->info.label, (const char *) entry.label, sizeof(item->info.label) - 1);
  145. // add it to the list
  146. if (last == NULL) {
  147. SLIST_INSERT_HEAD(&new_partitions_list, item, next);
  148. } else {
  149. SLIST_INSERT_AFTER(last, item, next);
  150. }
  151. last = item;
  152. }
  153. #if CONFIG_PARTITION_TABLE_MD5
  154. if (md5_part == NULL) {
  155. ESP_LOGE(TAG, "No MD5 found in partition table");
  156. err = ESP_ERR_NOT_FOUND;
  157. } else {
  158. stored_md5 = md5_part + ESP_PARTITION_MD5_OFFSET;
  159. esp_rom_md5_final(calc_md5, &context);
  160. #if !CONFIG_IDF_TARGET_LINUX
  161. ESP_LOG_BUFFER_HEXDUMP("calculated md5", calc_md5, ESP_ROM_MD5_DIGEST_LEN, ESP_LOG_VERBOSE);
  162. ESP_LOG_BUFFER_HEXDUMP("stored md5", stored_md5, ESP_ROM_MD5_DIGEST_LEN, ESP_LOG_VERBOSE);
  163. #endif
  164. if (memcmp(calc_md5, stored_md5, ESP_ROM_MD5_DIGEST_LEN) != 0) {
  165. ESP_LOGE(TAG, "Partition table MD5 mismatch");
  166. err = ESP_ERR_INVALID_STATE;
  167. } else {
  168. ESP_LOGV(TAG, "Partition table MD5 verified");
  169. }
  170. }
  171. #endif
  172. if (err == ESP_OK) {
  173. /* Don't copy the list to the static variable unless it's verified */
  174. s_partition_list = new_partitions_list;
  175. } else {
  176. /* Otherwise, free all the memory we just allocated */
  177. partition_list_item_t *it = new_partitions_list.slh_first;
  178. while (it) {
  179. partition_list_item_t *next = it->next.sle_next;
  180. free(it);
  181. it = next;
  182. }
  183. }
  184. #if !CONFIG_IDF_TARGET_LINUX
  185. spi_flash_munmap(handle);
  186. #endif
  187. return err;
  188. }
  189. static esp_err_t ensure_partitions_loaded(void)
  190. {
  191. esp_err_t err = ESP_OK;
  192. if (SLIST_EMPTY(&s_partition_list)) {
  193. // only lock if list is empty (and check again after acquiring lock)
  194. _lock_acquire(&s_partition_list_lock);
  195. if (SLIST_EMPTY(&s_partition_list)) {
  196. ESP_LOGV(TAG, "Loading the partition table");
  197. err = load_partitions();
  198. if (err != ESP_OK) {
  199. ESP_LOGE(TAG, "load_partitions returned 0x%x", err);
  200. }
  201. }
  202. _lock_release(&s_partition_list_lock);
  203. }
  204. return err;
  205. }
  206. static esp_partition_iterator_opaque_t *iterator_create(esp_partition_type_t type,
  207. esp_partition_subtype_t subtype, const char *label)
  208. {
  209. esp_partition_iterator_opaque_t *it =
  210. (esp_partition_iterator_opaque_t *) malloc(sizeof(esp_partition_iterator_opaque_t));
  211. if (it == NULL) {
  212. return NULL;
  213. }
  214. it->type = type;
  215. it->subtype = subtype;
  216. it->label = label;
  217. it->next_item = SLIST_FIRST(&s_partition_list);
  218. it->info = NULL;
  219. return it;
  220. }
  221. esp_partition_iterator_t esp_partition_find(esp_partition_type_t type,
  222. esp_partition_subtype_t subtype, const char *label)
  223. {
  224. if (ensure_partitions_loaded() != ESP_OK) {
  225. return NULL;
  226. }
  227. // Searching for a specific subtype without specifying the type doesn't make
  228. // sense, and is likely a usage error.
  229. if (type == ESP_PARTITION_TYPE_ANY && subtype != ESP_PARTITION_SUBTYPE_ANY) {
  230. return NULL;
  231. }
  232. // create an iterator pointing to the start of the list
  233. // (next item will be the first one)
  234. esp_partition_iterator_t it = iterator_create(type, subtype, label);
  235. if (it == NULL) {
  236. return NULL;
  237. }
  238. // advance iterator to the next item which matches constraints
  239. it = esp_partition_next(it);
  240. // if nothing found, it == NULL and iterator has been released
  241. return it;
  242. }
  243. esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t it)
  244. {
  245. assert(it);
  246. // iterator reached the end of linked list?
  247. if (it->next_item == NULL) {
  248. esp_partition_iterator_release(it);
  249. return NULL;
  250. }
  251. _lock_acquire(&s_partition_list_lock);
  252. for (; it->next_item != NULL; it->next_item = SLIST_NEXT(it->next_item, next)) {
  253. esp_partition_t *p = &it->next_item->info;
  254. if (it->type != ESP_PARTITION_TYPE_ANY && it->type != p->type) {
  255. continue;
  256. }
  257. if (it->subtype != ESP_PARTITION_SUBTYPE_ANY && it->subtype != p->subtype) {
  258. continue;
  259. }
  260. if (it->label != NULL && strcmp(it->label, p->label) != 0) {
  261. continue;
  262. }
  263. // all constraints match, bail out
  264. break;
  265. }
  266. _lock_release(&s_partition_list_lock);
  267. if (it->next_item == NULL) {
  268. esp_partition_iterator_release(it);
  269. return NULL;
  270. }
  271. it->info = &it->next_item->info;
  272. it->next_item = SLIST_NEXT(it->next_item, next);
  273. return it;
  274. }
  275. const esp_partition_t *esp_partition_find_first(esp_partition_type_t type,
  276. esp_partition_subtype_t subtype, const char *label)
  277. {
  278. esp_partition_iterator_t it = esp_partition_find(type, subtype, label);
  279. if (it == NULL) {
  280. return NULL;
  281. }
  282. const esp_partition_t *res = esp_partition_get(it);
  283. esp_partition_iterator_release(it);
  284. return res;
  285. }
  286. void esp_partition_iterator_release(esp_partition_iterator_t iterator)
  287. {
  288. // iterator == NULL is okay
  289. free(iterator);
  290. }
  291. const esp_partition_t *esp_partition_get(esp_partition_iterator_t iterator)
  292. {
  293. assert(iterator != NULL);
  294. return iterator->info;
  295. }
  296. const esp_partition_t *esp_partition_verify(const esp_partition_t *partition)
  297. {
  298. assert(partition != NULL);
  299. const char *label = (strlen(partition->label) > 0) ? partition->label : NULL;
  300. esp_partition_iterator_t it = esp_partition_find(partition->type,
  301. partition->subtype,
  302. label);
  303. while (it != NULL) {
  304. const esp_partition_t *p = esp_partition_get(it);
  305. /* Can't memcmp() whole structure here as padding contents may be different */
  306. if (p->flash_chip == partition->flash_chip
  307. && p->address == partition->address
  308. && partition->size == p->size
  309. && partition->encrypted == p->encrypted) {
  310. esp_partition_iterator_release(it);
  311. return p;
  312. }
  313. it = esp_partition_next(it);
  314. }
  315. esp_partition_iterator_release(it);
  316. return NULL;
  317. }
  318. esp_err_t esp_partition_register_external(esp_flash_t *flash_chip, size_t offset, size_t size,
  319. const char *label, esp_partition_type_t type, esp_partition_subtype_t subtype,
  320. const esp_partition_t **out_partition)
  321. {
  322. if (out_partition != NULL) {
  323. *out_partition = NULL;
  324. }
  325. if (offset + size > flash_chip->size) {
  326. return ESP_ERR_INVALID_SIZE;
  327. }
  328. esp_err_t err = ensure_partitions_loaded();
  329. if (err != ESP_OK) {
  330. return err;
  331. }
  332. partition_list_item_t *item = (partition_list_item_t *) calloc(sizeof(partition_list_item_t), 1);
  333. if (item == NULL) {
  334. return ESP_ERR_NO_MEM;
  335. }
  336. item->info.flash_chip = flash_chip;
  337. item->info.address = offset;
  338. item->info.size = size;
  339. item->info.type = type;
  340. item->info.subtype = subtype;
  341. item->info.encrypted = false;
  342. item->user_registered = true;
  343. strlcpy(item->info.label, label, sizeof(item->info.label));
  344. _lock_acquire(&s_partition_list_lock);
  345. partition_list_item_t *it = NULL;
  346. partition_list_item_t *last = NULL;
  347. SLIST_FOREACH(it, &s_partition_list, next) {
  348. /* Check if the new partition overlaps an existing one */
  349. if (it->info.flash_chip == flash_chip &&
  350. bootloader_util_regions_overlap(offset, offset + size,
  351. it->info.address, it->info.address + it->info.size)) {
  352. _lock_release(&s_partition_list_lock);
  353. free(item);
  354. return ESP_ERR_INVALID_ARG;
  355. }
  356. last = it;
  357. }
  358. if (last == NULL) {
  359. SLIST_INSERT_HEAD(&s_partition_list, item, next);
  360. } else {
  361. SLIST_INSERT_AFTER(last, item, next);
  362. }
  363. _lock_release(&s_partition_list_lock);
  364. if (out_partition != NULL) {
  365. *out_partition = &item->info;
  366. }
  367. return ESP_OK;
  368. }
  369. esp_err_t esp_partition_deregister_external(const esp_partition_t *partition)
  370. {
  371. esp_err_t result = ESP_ERR_NOT_FOUND;
  372. _lock_acquire(&s_partition_list_lock);
  373. partition_list_item_t *it;
  374. SLIST_FOREACH(it, &s_partition_list, next) {
  375. if (&it->info == partition) {
  376. if (!it->user_registered) {
  377. result = ESP_ERR_INVALID_ARG;
  378. break;
  379. }
  380. SLIST_REMOVE(&s_partition_list, it, partition_list_item_, next);
  381. free(it);
  382. result = ESP_OK;
  383. break;
  384. }
  385. }
  386. _lock_release(&s_partition_list_lock);
  387. return result;
  388. }