]> granicus.if.org Git - esp-idf/blob - components/spi_flash/partition.c
Merge branch 'bugfix/btdm_coex_hw_blocking' into 'master'
[esp-idf] / components / spi_flash / partition.c
1 // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
2 //
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
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
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.
14
15 #include <stdlib.h>
16 #include <assert.h>
17 #include <string.h>
18 #include <stdio.h>
19 #include <sys/lock.h>
20 #include "esp_flash_partitions.h"
21 #include "esp_attr.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"
26 #include "esp_log.h"
27 #include "bootloader_common.h"
28
29 #define HASH_LEN 32 /* SHA-256 digest length */
30
31 #ifndef NDEBUG
32 // Enable built-in checks in queue.h in debug builds
33 #define INVARIANTS
34 #endif
35 #include "rom/queue.h"
36
37
38 typedef struct partition_list_item_ {
39     esp_partition_t info;
40     SLIST_ENTRY(partition_list_item_) next;
41 } partition_list_item_t;
42
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;
50
51
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();
54
55
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;
59
60
61 esp_partition_iterator_t esp_partition_find(esp_partition_type_t type,
62         esp_partition_subtype_t subtype, const char* label)
63 {
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();
70         }
71         _lock_release(&s_partition_list_lock);
72         if (err != ESP_OK) {
73             return NULL;
74         }
75     }
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
82     return it;
83 }
84
85 esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t it)
86 {
87     assert(it);
88     // iterator reached the end of linked list?
89     if (it->next_item == NULL) {
90         esp_partition_iterator_release(it);
91         return NULL;
92     }
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) {
97             continue;
98         }
99         if (it->subtype != 0xff && it->subtype != p->subtype) {
100             continue;
101         }
102         if (it->label != NULL && strcmp(it->label, p->label) != 0) {
103             continue;
104         }
105         // all constraints match, bail out
106         break;
107     }
108     _lock_release(&s_partition_list_lock);
109     if (it->next_item == NULL) {
110         esp_partition_iterator_release(it);
111         return NULL;
112     }
113     it->info = &it->next_item->info;
114     it->next_item = SLIST_NEXT(it->next_item, next);
115     return it;
116 }
117
118 const esp_partition_t* esp_partition_find_first(esp_partition_type_t type,
119         esp_partition_subtype_t subtype, const char* label)
120 {
121     esp_partition_iterator_t it = esp_partition_find(type, subtype, label);
122     if (it == NULL) {
123         return NULL;
124     }
125     const esp_partition_t* res = esp_partition_get(it);
126     esp_partition_iterator_release(it);
127     return res;
128 }
129
130 static esp_partition_iterator_opaque_t* iterator_create(esp_partition_type_t type,
131         esp_partition_subtype_t subtype, const char* label)
132 {
133     esp_partition_iterator_opaque_t* it =
134             (esp_partition_iterator_opaque_t*) malloc(sizeof(esp_partition_iterator_opaque_t));
135     it->type = type;
136     it->subtype = subtype;
137     it->label = label;
138     it->next_item = SLIST_FIRST(&s_partition_list);
139     it->info = NULL;
140     return it;
141 }
142
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()
146 {
147     const uint32_t* ptr;
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);
152     if (err != ESP_OK) {
153         return err;
154     }
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) {
163             break;
164         }
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;
179         }
180
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
185         if (last == NULL) {
186             SLIST_INSERT_HEAD(&s_partition_list, item, next);
187         } else {
188             SLIST_INSERT_AFTER(last, item, next);
189         }
190         last = item;
191     }
192     spi_flash_munmap(handle);
193     return ESP_OK;
194 }
195
196 void esp_partition_iterator_release(esp_partition_iterator_t iterator)
197 {
198     // iterator == NULL is okay
199     free(iterator);
200 }
201
202 const esp_partition_t* esp_partition_get(esp_partition_iterator_t iterator)
203 {
204     assert(iterator != NULL);
205     return iterator->info;
206 }
207
208 const esp_partition_t *esp_partition_verify(const esp_partition_t *partition)
209 {
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,
213                                                      partition->subtype,
214                                                      label);
215     while (it != NULL) {
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);
222             return p;
223         }
224         it = esp_partition_next(it);
225     }
226     esp_partition_iterator_release(it);
227     return NULL;
228 }
229
230 esp_err_t esp_partition_read(const esp_partition_t* partition,
231         size_t src_offset, void* dst, size_t size)
232 {
233     assert(partition != NULL);
234     if (src_offset > partition->size) {
235         return ESP_ERR_INVALID_ARG;
236     }
237     if (src_offset + size > partition->size) {
238         return ESP_ERR_INVALID_SIZE;
239     }
240
241     if (!partition->encrypted) {
242         return spi_flash_read(partition->address + src_offset, dst, size);
243     } else {
244         /* Encrypted partitions need to be read via a cache mapping */
245         const void *buf;
246         spi_flash_mmap_handle_t handle;
247         esp_err_t err;
248
249         err = esp_partition_mmap(partition, src_offset, size,
250                                  SPI_FLASH_MMAP_DATA, &buf, &handle);
251         if (err != ESP_OK) {
252             return err;
253         }
254         memcpy(dst, buf, size);
255         spi_flash_munmap(handle);
256         return ESP_OK;
257     }
258 }
259
260 esp_err_t esp_partition_write(const esp_partition_t* partition,
261                              size_t dst_offset, const void* src, size_t size)
262 {
263     assert(partition != NULL);
264     if (dst_offset > partition->size) {
265         return ESP_ERR_INVALID_ARG;
266     }
267     if (dst_offset + size > partition->size) {
268         return ESP_ERR_INVALID_SIZE;
269     }
270     dst_offset = partition->address + dst_offset;
271     if (partition->encrypted) {
272         return spi_flash_write_encrypted(dst_offset, src, size);
273     } else {
274         return spi_flash_write(dst_offset, src, size);
275     }
276 }
277
278 esp_err_t esp_partition_erase_range(const esp_partition_t* partition,
279                                     size_t start_addr, size_t size)
280 {
281     assert(partition != NULL);
282     if (start_addr > partition->size) {
283         return ESP_ERR_INVALID_ARG;
284     }
285     if (start_addr + size > partition->size) {
286         return ESP_ERR_INVALID_SIZE;
287     }
288     if (size % SPI_FLASH_SEC_SIZE != 0) {
289         return ESP_ERR_INVALID_SIZE;
290     }
291     if (start_addr % SPI_FLASH_SEC_SIZE != 0) {
292         return ESP_ERR_INVALID_ARG;
293     }
294     return spi_flash_erase_range(partition->address + start_addr, size);
295
296 }
297
298 /*
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.
301  *
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.
305  */
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)
309 {
310     assert(partition != NULL);
311     if (offset > partition->size) {
312         return ESP_ERR_INVALID_ARG;
313     }
314     if (offset + size > partition->size) {
315         return ESP_ERR_INVALID_SIZE;
316     }
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
323     if (rc == ESP_OK) {
324         *out_ptr = (void*) (((ptrdiff_t) *out_ptr) + region_offset);
325     }
326     return rc;
327 }
328
329 esp_err_t esp_partition_get_sha256(const esp_partition_t *partition, uint8_t *sha_256)
330 {
331     return bootloader_common_get_sha256_of_partition(partition->address, partition->size, partition->type, sha_256);
332 }
333
334 bool esp_partition_check_identity(const esp_partition_t *partition_1, const esp_partition_t *partition_2)
335 {
336     uint8_t sha_256[2][HASH_LEN] = { 0 };
337
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) {
340
341         if (memcmp(sha_256[0], sha_256[1], HASH_LEN) == 0) {
342             // The partitions are identity
343             return true;
344         }
345     }
346     return false;
347 }