1 // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
20 #include "esp_flash_partitions.h"
22 #include "esp_flash_data_types.h"
23 #include "esp_spi_flash.h"
24 #include "esp_partition.h"
25 #include "esp_flash_encrypt.h"
27 #include "bootloader_common.h"
29 #define HASH_LEN 32 /* SHA-256 digest length */
32 // Enable built-in checks in queue.h in debug builds
35 #include "rom/queue.h"
38 typedef struct partition_list_item_ {
40 SLIST_ENTRY(partition_list_item_) next;
41 } partition_list_item_t;
43 typedef struct esp_partition_iterator_opaque_ {
44 esp_partition_type_t type; // requested type
45 esp_partition_subtype_t subtype; // requested subtype
46 const char* label; // requested label (can be NULL)
47 partition_list_item_t* next_item; // next item to iterate to
48 esp_partition_t* info; // pointer to info (it is redundant, but makes code more readable)
49 } esp_partition_iterator_opaque_t;
52 static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type, esp_partition_subtype_t subtype, const char* label);
53 static esp_err_t load_partitions();
56 static SLIST_HEAD(partition_list_head_, partition_list_item_) s_partition_list =
57 SLIST_HEAD_INITIALIZER(s_partition_list);
58 static _lock_t s_partition_list_lock;
61 esp_partition_iterator_t esp_partition_find(esp_partition_type_t type,
62 esp_partition_subtype_t subtype, const char* label)
64 if (SLIST_EMPTY(&s_partition_list)) {
65 // only lock if list is empty (and check again after acquiring lock)
66 _lock_acquire(&s_partition_list_lock);
67 esp_err_t err = ESP_OK;
68 if (SLIST_EMPTY(&s_partition_list)) {
69 err = load_partitions();
71 _lock_release(&s_partition_list_lock);
76 // create an iterator pointing to the start of the list
77 // (next item will be the first one)
78 esp_partition_iterator_t it = iterator_create(type, subtype, label);
79 // advance iterator to the next item which matches constraints
80 it = esp_partition_next(it);
81 // if nothing found, it == NULL and iterator has been released
85 esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t it)
88 // iterator reached the end of linked list?
89 if (it->next_item == NULL) {
90 esp_partition_iterator_release(it);
93 _lock_acquire(&s_partition_list_lock);
94 for (; it->next_item != NULL; it->next_item = SLIST_NEXT(it->next_item, next)) {
95 esp_partition_t* p = &it->next_item->info;
96 if (it->type != p->type) {
99 if (it->subtype != 0xff && it->subtype != p->subtype) {
102 if (it->label != NULL && strcmp(it->label, p->label) != 0) {
105 // all constraints match, bail out
108 _lock_release(&s_partition_list_lock);
109 if (it->next_item == NULL) {
110 esp_partition_iterator_release(it);
113 it->info = &it->next_item->info;
114 it->next_item = SLIST_NEXT(it->next_item, next);
118 const esp_partition_t* esp_partition_find_first(esp_partition_type_t type,
119 esp_partition_subtype_t subtype, const char* label)
121 esp_partition_iterator_t it = esp_partition_find(type, subtype, label);
125 const esp_partition_t* res = esp_partition_get(it);
126 esp_partition_iterator_release(it);
130 static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type,
131 esp_partition_subtype_t subtype, const char* label)
133 esp_partition_iterator_opaque_t* it =
134 (esp_partition_iterator_opaque_t*) malloc(sizeof(esp_partition_iterator_opaque_t));
136 it->subtype = subtype;
138 it->next_item = SLIST_FIRST(&s_partition_list);
143 // Create linked list of partition_list_item_t structures.
144 // This function is called only once, with s_partition_list_lock taken.
145 static esp_err_t load_partitions()
148 spi_flash_mmap_handle_t handle;
149 // map 64kB block where partition table is located
150 esp_err_t err = spi_flash_mmap(ESP_PARTITION_TABLE_OFFSET & 0xffff0000,
151 SPI_FLASH_SEC_SIZE, SPI_FLASH_MMAP_DATA, (const void**) &ptr, &handle);
155 // calculate partition address within mmap-ed region
156 const esp_partition_info_t* it = (const esp_partition_info_t*)
157 (ptr + (ESP_PARTITION_TABLE_OFFSET & 0xffff) / sizeof(*ptr));
158 const esp_partition_info_t* end = it + SPI_FLASH_SEC_SIZE / sizeof(*it);
159 // tail of the linked list of partitions
160 partition_list_item_t* last = NULL;
161 for (; it != end; ++it) {
162 if (it->magic != ESP_PARTITION_MAGIC) {
165 // allocate new linked list item and populate it with data from partition table
166 partition_list_item_t* item = (partition_list_item_t*) malloc(sizeof(partition_list_item_t));
167 item->info.address = it->pos.offset;
168 item->info.size = it->pos.size;
169 item->info.type = it->type;
170 item->info.subtype = it->subtype;
171 item->info.encrypted = it->flags & PART_FLAG_ENCRYPTED;
172 if (esp_flash_encryption_enabled() && (
173 it->type == PART_TYPE_APP
174 || (it->type == PART_TYPE_DATA && it->subtype == PART_SUBTYPE_DATA_OTA)
175 || (it->type == PART_TYPE_DATA && it->subtype == PART_SUBTYPE_DATA_NVS_KEYS))) {
176 /* If encryption is turned on, all app partitions and OTA data
177 are always encrypted */
178 item->info.encrypted = true;
181 // it->label may not be zero-terminated
182 strncpy(item->info.label, (const char*) it->label, sizeof(item->info.label) - 1);
183 item->info.label[sizeof(it->label)] = 0;
184 // add it to the list
186 SLIST_INSERT_HEAD(&s_partition_list, item, next);
188 SLIST_INSERT_AFTER(last, item, next);
192 spi_flash_munmap(handle);
196 void esp_partition_iterator_release(esp_partition_iterator_t iterator)
198 // iterator == NULL is okay
202 const esp_partition_t* esp_partition_get(esp_partition_iterator_t iterator)
204 assert(iterator != NULL);
205 return iterator->info;
208 const esp_partition_t *esp_partition_verify(const esp_partition_t *partition)
210 assert(partition != NULL);
211 const char *label = (strlen(partition->label) > 0) ? partition->label : NULL;
212 esp_partition_iterator_t it = esp_partition_find(partition->type,
216 const esp_partition_t *p = esp_partition_get(it);
217 /* Can't memcmp() whole structure here as padding contents may be different */
218 if (p->address == partition->address
219 && partition->size == p->size
220 && partition->encrypted == p->encrypted) {
221 esp_partition_iterator_release(it);
224 it = esp_partition_next(it);
226 esp_partition_iterator_release(it);
230 esp_err_t esp_partition_read(const esp_partition_t* partition,
231 size_t src_offset, void* dst, size_t size)
233 assert(partition != NULL);
234 if (src_offset > partition->size) {
235 return ESP_ERR_INVALID_ARG;
237 if (src_offset + size > partition->size) {
238 return ESP_ERR_INVALID_SIZE;
241 if (!partition->encrypted) {
242 return spi_flash_read(partition->address + src_offset, dst, size);
244 /* Encrypted partitions need to be read via a cache mapping */
246 spi_flash_mmap_handle_t handle;
249 err = esp_partition_mmap(partition, src_offset, size,
250 SPI_FLASH_MMAP_DATA, &buf, &handle);
254 memcpy(dst, buf, size);
255 spi_flash_munmap(handle);
260 esp_err_t esp_partition_write(const esp_partition_t* partition,
261 size_t dst_offset, const void* src, size_t size)
263 assert(partition != NULL);
264 if (dst_offset > partition->size) {
265 return ESP_ERR_INVALID_ARG;
267 if (dst_offset + size > partition->size) {
268 return ESP_ERR_INVALID_SIZE;
270 dst_offset = partition->address + dst_offset;
271 if (partition->encrypted) {
272 return spi_flash_write_encrypted(dst_offset, src, size);
274 return spi_flash_write(dst_offset, src, size);
278 esp_err_t esp_partition_erase_range(const esp_partition_t* partition,
279 size_t start_addr, size_t size)
281 assert(partition != NULL);
282 if (start_addr > partition->size) {
283 return ESP_ERR_INVALID_ARG;
285 if (start_addr + size > partition->size) {
286 return ESP_ERR_INVALID_SIZE;
288 if (size % SPI_FLASH_SEC_SIZE != 0) {
289 return ESP_ERR_INVALID_SIZE;
291 if (start_addr % SPI_FLASH_SEC_SIZE != 0) {
292 return ESP_ERR_INVALID_ARG;
294 return spi_flash_erase_range(partition->address + start_addr, size);
299 * Note: current implementation ignores the possibility of multiple regions in the same partition being
300 * mapped. Reference counting and address space re-use is delegated to spi_flash_mmap.
302 * If this becomes a performance issue (i.e. if we need to map multiple regions within the partition),
303 * we can add esp_partition_mmapv which will accept an array of offsets and sizes, and return array of
304 * mmaped pointers, and a single handle for all these regions.
306 esp_err_t esp_partition_mmap(const esp_partition_t* partition, uint32_t offset, uint32_t size,
307 spi_flash_mmap_memory_t memory,
308 const void** out_ptr, spi_flash_mmap_handle_t* out_handle)
310 assert(partition != NULL);
311 if (offset > partition->size) {
312 return ESP_ERR_INVALID_ARG;
314 if (offset + size > partition->size) {
315 return ESP_ERR_INVALID_SIZE;
317 size_t phys_addr = partition->address + offset;
318 // offset within 64kB block
319 size_t region_offset = phys_addr & 0xffff;
320 size_t mmap_addr = phys_addr & 0xffff0000;
321 esp_err_t rc = spi_flash_mmap(mmap_addr, size+region_offset, memory, out_ptr, out_handle);
322 // adjust returned pointer to point to the correct offset
324 *out_ptr = (void*) (((ptrdiff_t) *out_ptr) + region_offset);
329 esp_err_t esp_partition_get_sha256(const esp_partition_t *partition, uint8_t *sha_256)
331 return bootloader_common_get_sha256_of_partition(partition->address, partition->size, partition->type, sha_256);
334 bool esp_partition_check_identity(const esp_partition_t *partition_1, const esp_partition_t *partition_2)
336 uint8_t sha_256[2][HASH_LEN] = { 0 };
338 if (esp_partition_get_sha256(partition_1, sha_256[0]) == ESP_OK &&
339 esp_partition_get_sha256(partition_2, sha_256[1]) == ESP_OK) {
341 if (memcmp(sha_256[0], sha_256[1], HASH_LEN) == 0) {
342 // The partitions are identity