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 #include <string.h>
15 #include "esp_partition.h"
16 #include "esp_log.h"
17 #include "esp_core_dump_types.h"
18 #include "core_dump_checksum.h"
19 #include "esp_flash_internal.h"
20 #include "esp_flash_encrypt.h"
21 #include "esp_rom_crc.h"
22 
23 #define BLANK_COREDUMP_SIZE 0xFFFFFFFF
24 
25 const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_flash";
26 
27 #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
28 
29 typedef struct _core_dump_partition_t
30 {
31     /* Core dump partition start. */
32     uint32_t start;
33     /* Core dump partition size. */
34     uint32_t size;
35     /* Flag set to true if the partition is encrypted. */
36     bool encrypted;
37 } core_dump_partition_t;
38 
39 typedef uint32_t core_dump_crc_t;
40 
41 typedef struct _core_dump_flash_config_t
42 {
43     /* Core dump partition config. */
44     core_dump_partition_t partition;
45     /* CRC of core dump partition config. */
46     core_dump_crc_t partition_config_crc;
47 } core_dump_flash_config_t;
48 
49 /* Core dump flash data. */
50 static core_dump_flash_config_t s_core_flash_config;
51 
52 #ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
53 #define ESP_COREDUMP_FLASH_WRITE(_off_, _data_, _len_)           spi_flash_write(_off_, _data_, _len_)
54 #define ESP_COREDUMP_FLASH_WRITE_ENCRYPTED(_off_, _data_, _len_) spi_flash_write_encrypted(_off_, _data_, _len_)
55 #define ESP_COREDUMP_FLASH_ERASE(_off_, _len_)                   spi_flash_erase_range(_off_, _len_)
56 #else
57 #define ESP_COREDUMP_FLASH_WRITE(_off_, _data_, _len_)           esp_flash_write(esp_flash_default_chip, _data_, _off_, _len_)
58 #define ESP_COREDUMP_FLASH_WRITE_ENCRYPTED(_off_, _data_, _len_) esp_flash_write_encrypted(esp_flash_default_chip, _off_, _data_, _len_)
59 #define ESP_COREDUMP_FLASH_ERASE(_off_, _len_)                   esp_flash_erase_region(esp_flash_default_chip, _off_, _len_)
60 #endif
61 
62 esp_err_t esp_core_dump_image_check(void);
63 static esp_err_t esp_core_dump_partition_and_size_get(const esp_partition_t **partition, uint32_t* size);
64 
esp_core_dump_flash_custom_write(uint32_t address,const void * buffer,uint32_t length)65 static esp_err_t esp_core_dump_flash_custom_write(uint32_t address, const void *buffer, uint32_t length)
66 {
67     esp_err_t err = ESP_OK;
68 
69     if (esp_flash_encryption_enabled() && s_core_flash_config.partition.encrypted) {
70         err = ESP_COREDUMP_FLASH_WRITE_ENCRYPTED(address, buffer, length);
71     } else {
72         err = ESP_COREDUMP_FLASH_WRITE(address, buffer, length);
73     }
74 
75     return err;
76 }
77 
esp_core_dump_calc_flash_config_crc(void)78 static inline core_dump_crc_t esp_core_dump_calc_flash_config_crc(void)
79 {
80     return esp_rom_crc32_le(0, (uint8_t const *)&s_core_flash_config.partition, sizeof(s_core_flash_config.partition));
81 }
82 
esp_core_dump_flash_init(void)83 void esp_core_dump_flash_init(void)
84 {
85     const esp_partition_t *core_part = NULL;
86 
87     /* Look for the core dump partition on the flash. */
88     ESP_COREDUMP_LOGI("Init core dump to flash");
89     core_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL);
90     if (!core_part) {
91         ESP_COREDUMP_LOGE("No core dump partition found!");
92         return;
93     }
94     ESP_COREDUMP_LOGI("Found partition '%s' @ %x %d bytes", core_part->label, core_part->address, core_part->size);
95     s_core_flash_config.partition.start      = core_part->address;
96     s_core_flash_config.partition.size       = core_part->size;
97     s_core_flash_config.partition.encrypted  = core_part->encrypted;
98     s_core_flash_config.partition_config_crc = esp_core_dump_calc_flash_config_crc();
99 }
100 
esp_core_dump_flash_write_data(core_dump_write_data_t * priv,uint8_t * data,uint32_t data_size)101 static esp_err_t esp_core_dump_flash_write_data(core_dump_write_data_t* priv, uint8_t* data, uint32_t data_size)
102 {
103     core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv;
104     esp_err_t err = ESP_OK;
105     uint32_t written = 0;
106     uint32_t wr_sz = 0;
107 
108     /* Make sure that the partition is large enough to hold the data. */
109     ESP_COREDUMP_ASSERT((wr_data->off + data_size) < s_core_flash_config.partition.size);
110 
111     if (wr_data->cached_bytes) {
112         /* Some bytes are in the cache, let's continue filling the cache
113          * with the data received as parameter. Let's calculate the maximum
114          * amount of bytes we can still fill the cache with. */
115         if ((COREDUMP_CACHE_SIZE - wr_data->cached_bytes) > data_size)
116             wr_sz = data_size;
117         else
118             wr_sz = COREDUMP_CACHE_SIZE - wr_data->cached_bytes;
119 
120         /* Append wr_sz bytes from data parameter to the cache. */
121         memcpy(&wr_data->cached_data[wr_data->cached_bytes], data, wr_sz);
122         wr_data->cached_bytes += wr_sz;
123 
124         if (wr_data->cached_bytes == COREDUMP_CACHE_SIZE) {
125             /* The cache is full, we can flush it to the flash. */
126             err = esp_core_dump_flash_custom_write(s_core_flash_config.partition.start + wr_data->off,
127                                                    wr_data->cached_data,
128                                                    COREDUMP_CACHE_SIZE);
129             if (err != ESP_OK) {
130                 ESP_COREDUMP_LOGE("Failed to write cached data to flash (%d)!", err);
131                 return err;
132             }
133             /* The offset of the next data that will be written onto the flash
134              * can now be increased. */
135             wr_data->off += COREDUMP_CACHE_SIZE;
136 
137             /* Update checksum with the newly written data on the flash. */
138             esp_core_dump_checksum_update(wr_data->checksum_ctx, &wr_data->cached_data, COREDUMP_CACHE_SIZE);
139 
140             /* Reset cache from the next use. */
141             wr_data->cached_bytes = 0;
142             memset(wr_data->cached_data, 0, COREDUMP_CACHE_SIZE);
143         }
144 
145         written += wr_sz;
146         data_size -= wr_sz;
147     }
148 
149     /* Figure out how many bytes we can write onto the flash directly, without
150      * using the cache. In our case the cache size is a multiple of the flash's
151      * minimum writing block size, so we will use it for our calculation.
152      * For example, if COREDUMP_CACHE_SIZE equals 32, here are interesting
153      * values:
154      * +---------+-----------------------+
155      * |         |       data_size       |
156      * +---------+---+----+----+----+----+
157      * |         | 0 | 31 | 32 | 40 | 64 |
158      * +---------+---+----+----+----+----+
159      * | (blocks | 0 | 0  | 1  | 1  | 2) |
160      * +---------+---+----+----+----+----+
161      * | wr_sz   | 0 | 0  | 32 | 32 | 64 |
162      * +---------+---+----+----+----+----+
163      */
164     wr_sz = (data_size / COREDUMP_CACHE_SIZE) * COREDUMP_CACHE_SIZE;
165     if (wr_sz) {
166         /* Write the contiguous amount of bytes to the flash,
167          * without using the cache */
168         err = esp_core_dump_flash_custom_write(s_core_flash_config.partition.start + wr_data->off, data + written, wr_sz);
169 
170         if (err != ESP_OK) {
171             ESP_COREDUMP_LOGE("Failed to write data to flash (%d)!", err);
172             return err;
173         }
174 
175         /* Update the checksum with the newly written bytes */
176         esp_core_dump_checksum_update(wr_data->checksum_ctx, data + written, wr_sz);
177         wr_data->off += wr_sz;
178         written += wr_sz;
179         data_size -= wr_sz;
180     }
181 
182     if (data_size > 0) {
183         /* There still some bytes from the data parameter that need to be sent,
184          * append it to cache in order to write them later. (i.e. when there
185          * will be enough bytes to fill the cache) */
186         memcpy(&wr_data->cached_data, data + written, data_size);
187         wr_data->cached_bytes = data_size;
188     }
189 
190     return ESP_OK;
191 }
192 
esp_core_dump_flash_write_prepare(core_dump_write_data_t * priv,uint32_t * data_len)193 static esp_err_t esp_core_dump_flash_write_prepare(core_dump_write_data_t *priv, uint32_t *data_len)
194 {
195     core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv;
196     esp_err_t err = ESP_OK;
197     uint32_t sec_num = 0;
198     uint32_t cs_len = 0;
199 
200     /* Get the length, in bytes, of the checksum. */
201     cs_len = esp_core_dump_checksum_size();
202 
203     /* At the end of the core dump file, a padding may be added, according to the
204      * cache size. We must take that padding into account. */
205     uint32_t padding = 0;
206     const uint32_t modulo = *data_len % COREDUMP_CACHE_SIZE;
207     if (modulo != 0) {
208         /* The data length is not a multiple of the cache size,
209          * so there will be a padding. */
210         padding = COREDUMP_CACHE_SIZE - modulo;
211     }
212 
213     /* Now we can check whether we have enough space in our core dump parition
214      * or not. */
215     if ((*data_len + padding + cs_len) > s_core_flash_config.partition.size) {
216         ESP_COREDUMP_LOGE("Not enough space to save core dump!");
217         return ESP_ERR_NO_MEM;
218     }
219 
220     /* We have enough space in the partition, add the padding and the checksum
221      * in the core dump file calculation. */
222     *data_len += padding + cs_len;
223 
224     memset(wr_data, 0, sizeof(core_dump_write_data_t));
225 
226     /* In order to erase the right amount of data in the flash, we have to
227      * calculate how many SPI flash sectors will be needed by the core dump
228      * file. */
229     sec_num = *data_len / SPI_FLASH_SEC_SIZE;
230     if (*data_len % SPI_FLASH_SEC_SIZE) {
231         sec_num++;
232     }
233 
234     /* Erase the amount of sectors needed. */
235     ESP_COREDUMP_LOGI("Erase flash %d bytes @ 0x%x", sec_num * SPI_FLASH_SEC_SIZE, s_core_flash_config.partition.start + 0);
236     ESP_COREDUMP_ASSERT(sec_num * SPI_FLASH_SEC_SIZE <= s_core_flash_config.partition.size);
237     err = ESP_COREDUMP_FLASH_ERASE(s_core_flash_config.partition.start + 0, sec_num * SPI_FLASH_SEC_SIZE);
238     if (err != ESP_OK) {
239         ESP_COREDUMP_LOGE("Failed to erase flash (%d)!", err);
240     }
241 
242     return err;
243 }
244 
esp_core_dump_flash_write_start(core_dump_write_data_t * priv)245 static esp_err_t esp_core_dump_flash_write_start(core_dump_write_data_t* priv)
246 {
247     core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv;
248     esp_core_dump_checksum_init(&wr_data->checksum_ctx);
249     return ESP_OK;
250 }
251 
esp_core_dump_flash_write_end(core_dump_write_data_t * priv)252 static esp_err_t esp_core_dump_flash_write_end(core_dump_write_data_t* priv)
253 {
254     esp_err_t err = ESP_OK;
255     core_dump_checksum_bytes checksum = NULL;
256     uint32_t cs_len = 0;
257     core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv;
258 
259     /* Get the size, in bytes of the checksum. */
260     cs_len  = esp_core_dump_checksum_size();
261 
262     /* Flush cached bytes, including the zero padding at the end (if any). */
263     if (wr_data->cached_bytes) {
264         err = esp_core_dump_flash_custom_write(s_core_flash_config.partition.start + wr_data->off,
265                                                wr_data->cached_data,
266                                                COREDUMP_CACHE_SIZE);
267 
268         if (err != ESP_OK) {
269             ESP_COREDUMP_LOGE("Failed to flush cached data to flash (%d)!", err);
270             return err;
271         }
272 
273         /* Update the checksum with the data written, including the padding. */
274         esp_core_dump_checksum_update(wr_data->checksum_ctx, wr_data->cached_data, COREDUMP_CACHE_SIZE);
275         wr_data->off += COREDUMP_CACHE_SIZE;
276         wr_data->cached_bytes = 0;
277     }
278 
279     /* All data have been written to the flash, the cache is now empty, we can
280      * terminate the checksum calculation. */
281     esp_core_dump_checksum_finish(wr_data->checksum_ctx, &checksum);
282 
283     /* Use the cache to write the checksum if its size doesn't match the requirements.
284      * (e.g. its size is not a multiple of 32) */
285     if (cs_len < COREDUMP_CACHE_SIZE) {
286         /* Copy the checksum into the cache. */
287         memcpy(wr_data->cached_data, checksum, cs_len);
288 
289         /* Fill the rest of the cache with zeros. */
290         memset(wr_data->cached_data + cs_len, 0, COREDUMP_CACHE_SIZE - cs_len);
291 
292         /* Finally, write the checksum on the flash, using the cache. */
293         err = esp_core_dump_flash_custom_write(s_core_flash_config.partition.start + wr_data->off,
294                                                wr_data->cached_data,
295                                                COREDUMP_CACHE_SIZE);
296     } else {
297         /* In that case, the length of the checksum must be a multiple of 16. */
298         ESP_COREDUMP_ASSERT(cs_len % 16 == 0);
299         err = esp_core_dump_flash_custom_write(s_core_flash_config.partition.start + wr_data->off, checksum, cs_len);
300     }
301 
302     if (err != ESP_OK) {
303         ESP_COREDUMP_LOGE("Failed to flush cached data to flash (%d)!", err);
304         return err;
305     }
306     wr_data->off += cs_len;
307     ESP_COREDUMP_LOGI("Write end offset 0x%x, check sum length %d", wr_data->off, cs_len);
308 
309     return err;
310 }
311 
esp_core_dump_to_flash(panic_info_t * info)312 void esp_core_dump_to_flash(panic_info_t *info)
313 {
314     static core_dump_write_config_t wr_cfg = { 0 };
315     static core_dump_write_data_t wr_data = { 0 };
316 
317     /* Check core dump partition configuration. */
318     core_dump_crc_t crc = esp_core_dump_calc_flash_config_crc();
319     if (s_core_flash_config.partition_config_crc != crc) {
320         ESP_COREDUMP_LOGE("Core dump flash config is corrupted! CRC=0x%x instead of 0x%x", crc, s_core_flash_config.partition_config_crc);
321         return;
322     }
323 
324     /* Make sure that the partition can at least hold the data length. */
325     if (s_core_flash_config.partition.start == 0 || s_core_flash_config.partition.size < sizeof(uint32_t)) {
326         ESP_COREDUMP_LOGE("Invalid flash partition config!");
327         return;
328     }
329 
330     /* Initialize non-OS flash access critical section. */
331     spi_flash_guard_set(&g_flash_guard_no_os_ops);
332     esp_flash_app_disable_protect(true);
333 
334     /* Register the callbacks that will be called later by the generic part. */
335     wr_cfg.prepare = esp_core_dump_flash_write_prepare;
336     wr_cfg.start = esp_core_dump_flash_write_start;
337     wr_cfg.end = esp_core_dump_flash_write_end;
338     wr_cfg.write = (esp_core_dump_flash_write_data_t) esp_core_dump_flash_write_data;
339     wr_cfg.priv = &wr_data;
340 
341     ESP_COREDUMP_LOGI("Save core dump to flash...");
342     esp_core_dump_write(info, &wr_cfg);
343     ESP_COREDUMP_LOGI("Core dump has been saved to flash.");
344 }
345 
esp_core_dump_init(void)346 void esp_core_dump_init(void)
347 {
348     esp_core_dump_flash_init();
349 
350 #if CONFIG_ESP_COREDUMP_CHECK_BOOT
351     const esp_partition_t *partition = 0;
352     uint32_t size = 0;
353 
354     if (esp_core_dump_image_check() == ESP_OK
355         && esp_core_dump_partition_and_size_get(&partition, &size) == ESP_OK) {
356         ESP_COREDUMP_LOGI("Found core dump %d bytes in flash @ 0x%x", size, partition->address);
357     }
358 #endif
359 }
360 
esp_core_dump_image_check(void)361 esp_err_t esp_core_dump_image_check(void)
362 {
363     esp_err_t err = ESP_OK;
364     const esp_partition_t *core_part = NULL;
365     core_dump_write_data_t wr_data = { 0 };
366     uint32_t size = 0;
367     uint32_t total_size = 0;
368     uint32_t offset = 0;
369     const uint32_t checksum_size = esp_core_dump_checksum_size();
370     core_dump_checksum_bytes checksum_calc = NULL;
371     /* Initialize the checksum we have to read from the flash to the biggest
372      * size we can have for a checksum. */
373     uint8_t checksum_read[COREDUMP_CHECKSUM_MAX_LEN] = { 0 };
374 
375     /* Assert that we won't have any problems with our checksum size. */
376     ESP_COREDUMP_DEBUG_ASSERT(checksum_size <= COREDUMP_CHECKSUM_MAX_LEN);
377 
378     /* Retrieve the partition and size. */
379     err = esp_core_dump_partition_and_size_get(&core_part, &total_size);
380     if (err != ESP_OK) {
381         return err;
382     }
383 
384     /* The final checksum, from the image, doesn't take part into the checksum
385      * calculation, so subtract it from the bytes we are going to read. */
386     size = total_size - checksum_size ;
387 
388     /* Initiate the checksum calculation for the coredump in the flash. */
389     esp_core_dump_checksum_init(&wr_data.checksum_ctx);
390 
391     while (size > 0) {
392         /* Use the cache in core_dump_write_data_t structure to read the
393          * partition. */
394         const uint32_t toread = (size < COREDUMP_CACHE_SIZE) ? size : COREDUMP_CACHE_SIZE;
395 
396         /* Read the content of the flash. */
397         err = esp_partition_read(core_part, offset, wr_data.cached_data, toread);
398         if (err != ESP_OK) {
399             ESP_COREDUMP_LOGE("Failed to read data from core dump (%d)!", err);
400             return err;
401         }
402 
403         /* Update the checksum according to what was just read. */
404         esp_core_dump_checksum_update(wr_data.checksum_ctx, wr_data.cached_data, toread);
405 
406         /* Move the offset forward and decrease the remaining size. */
407         offset += toread;
408         size -= toread;
409     }
410 
411     /* The coredump has been totally read, finish the checksum calculation. */
412     esp_core_dump_checksum_finish(wr_data.checksum_ctx, &checksum_calc);
413 
414     /* Read the checksum from the flash and compare to the one just
415      * calculated. */
416     err = esp_partition_read(core_part, total_size - checksum_size, checksum_read, checksum_size);
417     if (err != ESP_OK) {
418         ESP_COREDUMP_LOGE("Failed to read checksum from core dump (%d)!", err);
419         return err;
420     }
421 
422     /* Compare the checksum read from the flash and the one just calculated. */
423     if (memcmp(checksum_calc, checksum_read, checksum_size) != 0) {
424         ESP_COREDUMP_LOGE("Core dump data check failed:");
425         esp_core_dump_print_checksum("Calculated checksum", checksum_calc);
426         esp_core_dump_print_checksum("Image checksum", checksum_read);
427         return ESP_ERR_INVALID_CRC;
428     } else {
429         ESP_COREDUMP_LOGI("Core dump data checksum is correct");
430     }
431 
432     return ESP_OK;
433 }
434 
435 #endif
436 
esp_core_dump_image_erase(void)437 esp_err_t esp_core_dump_image_erase(void)
438 {
439     /* If flash is encrypted, we can only write blocks of 16 bytes, let's always
440      * write a 16-byte buffer. */
441     uint32_t helper[4] = { BLANK_COREDUMP_SIZE };
442     _Static_assert(sizeof(helper) % 16 == 0, "esp_partition_write() needs multiple of 16 bytes long buffers");
443 
444     /* Find the partition that could potentially contain a (previous) core dump. */
445     const esp_partition_t *core_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
446                                                                 ESP_PARTITION_SUBTYPE_DATA_COREDUMP,
447                                                                 NULL);
448     if (!core_part) {
449         ESP_LOGE(TAG, "No core dump partition found!");
450         return ESP_ERR_NOT_FOUND;
451     }
452     if (core_part->size < sizeof(uint32_t)) {
453         ESP_LOGE(TAG, "Too small core dump partition!");
454         return ESP_ERR_INVALID_SIZE;
455     }
456 
457     esp_err_t err = ESP_OK;
458     err = esp_partition_erase_range(core_part, 0, core_part->size);
459     if (err != ESP_OK) {
460         ESP_LOGE(TAG, "Failed to erase core dump partition (%d)!", err);
461         return err;
462     }
463 
464     err = esp_partition_write(core_part, 0, helper, sizeof(helper));
465     if (err != ESP_OK) {
466         ESP_LOGE(TAG, "Failed to write core dump partition size (%d)!", err);
467     }
468 
469     return err;
470 }
471 
esp_core_dump_partition_and_size_get(const esp_partition_t ** partition,uint32_t * size)472 static esp_err_t esp_core_dump_partition_and_size_get(const esp_partition_t **partition, uint32_t* size)
473 {
474     uint32_t core_size = 0;
475     const esp_partition_t *core_part = NULL;
476 
477     /* Check the arguments, at least one should be provided. */
478     if (partition == NULL && size == NULL) {
479         return ESP_ERR_INVALID_ARG;
480     }
481 
482     /* Find the partition that could potentially contain a (previous) core dump. */
483     core_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
484                                          ESP_PARTITION_SUBTYPE_DATA_COREDUMP,
485                                          NULL);
486     if (core_part == NULL) {
487         ESP_COREDUMP_LOGE("No core dump partition found!");
488         return ESP_ERR_NOT_FOUND;
489     }
490     if (core_part->size < sizeof(uint32_t)) {
491         ESP_COREDUMP_LOGE("Too small core dump partition!");
492         return ESP_ERR_INVALID_SIZE;
493     }
494 
495     /* The partition has been found, get its first uint32_t value, which
496      * describes the core dump file size. */
497     esp_err_t err = esp_partition_read(core_part, 0, &core_size, sizeof(uint32_t));
498     if (err != ESP_OK) {
499         ESP_COREDUMP_LOGE("Failed to read core dump data size (%d)!", err);
500         return err;
501     }
502 
503     /* Verify that the size read from the flash is not corrupted. */
504     if (core_size == 0xFFFFFFFF) {
505         ESP_COREDUMP_LOGD("Blank core dump partition!");
506         return ESP_ERR_INVALID_SIZE;
507     }
508 
509     if ((core_size < sizeof(uint32_t)) || (core_size > core_part->size)) {
510         ESP_COREDUMP_LOGE("Incorrect size of core dump image: %d", core_size);
511         return ESP_ERR_INVALID_SIZE;
512     }
513 
514     /* Return the values if needed. */
515     if (partition != NULL) {
516         *partition = core_part;
517     }
518 
519     if (size != NULL) {
520         *size = core_size;
521     }
522 
523     return ESP_OK;
524 }
525 
esp_core_dump_image_get(size_t * out_addr,size_t * out_size)526 esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size)
527 {
528     esp_err_t err = ESP_OK;
529     uint32_t size = 0;
530     const esp_partition_t *core_part = NULL;
531 
532     /* Check the validity of the parameters. */
533     if (out_addr == NULL || out_size == NULL) {
534         return  ESP_ERR_INVALID_ARG;
535     }
536 
537     /* Retrieve the partition and size. */
538     err = esp_core_dump_partition_and_size_get(&core_part, &size);
539 
540     if (err != ESP_OK) {
541         return err;
542     }
543 
544     /* Save the address. */
545     *out_addr = core_part->address;
546 
547     /* Save the size read. */
548     *out_size = size;
549 
550     return ESP_OK;
551 }
552