1 /*
2  * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdint.h>
8 #include <string.h>
9 
10 #include "esp_attr.h"
11 #include "esp_err.h"
12 
13 #include "esp_system.h"
14 #include "esp_log.h"
15 
16 #include "sdkconfig.h"
17 
18 #include "soc/soc_caps.h"
19 #include "soc/chip_revision.h"
20 #include "hal/wdt_hal.h"
21 #include "hal/uart_types.h"
22 #include "hal/uart_ll.h"
23 #include "hal/efuse_hal.h"
24 
25 #include "esp_heap_caps_init.h"
26 #include "spi_flash_mmap.h"
27 #include "esp_flash_internal.h"
28 #include "esp_newlib.h"
29 #include "esp_timer.h"
30 #include "esp_efuse.h"
31 #include "esp_efuse_table.h"
32 #include "esp_flash_encrypt.h"
33 #include "esp_secure_boot.h"
34 #include "esp_xt_wdt.h"
35 #include "esp_cpu.h"
36 
37 #include "esp_partition.h"
38 
39 /***********************************************/
40 // Headers for other components init functions
41 #if CONFIG_SW_COEXIST_ENABLE || CONFIG_EXTERNAL_COEX_ENABLE
42 #include "private/esp_coexist_internal.h"
43 #endif
44 
45 #if __has_include("esp_app_desc.h")
46 #define WITH_APP_IMAGE_INFO
47 #include "esp_app_desc.h"
48 #endif
49 
50 #if CONFIG_ESP_COREDUMP_ENABLE
51 #include "esp_core_dump.h"
52 #endif
53 
54 #include "esp_private/dbg_stubs.h"
55 
56 #if CONFIG_PM_ENABLE
57 #include "esp_pm.h"
58 #include "esp_private/pm_impl.h"
59 #endif
60 
61 #if CONFIG_VFS_SUPPORT_IO
62 #include "esp_vfs_dev.h"
63 #include "esp_vfs_console.h"
64 #endif
65 
66 #include "esp_pthread.h"
67 #include "esp_private/esp_clk.h"
68 #include "esp_private/spi_flash_os.h"
69 #include "esp_private/brownout.h"
70 
71 #include "esp_rom_caps.h"
72 #include "esp_rom_sys.h"
73 
74 #if SOC_BOD_SUPPORTED
75 #include "hal/brownout_ll.h"
76 #endif
77 
78 #if CONFIG_SPIRAM
79 #include "esp_psram.h"
80 #include "esp_private/esp_psram_extram.h"
81 #endif
82 /***********************************************/
83 
84 #include "esp_private/startup_internal.h"
85 
86 // Ensure that system configuration matches the underlying number of cores.
87 // This should enable us to avoid checking for both everytime.
88 #if !(SOC_CPU_CORES_NUM > 1) && !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
89     #error "System has been configured to run on multiple cores, but target SoC only has a single core."
90 #endif
91 
92 // Set efuse ROM_LOG_MODE on first boot
93 //
94 // For CONFIG_BOOT_ROM_LOG_ALWAYS_ON (default) or undefined (ESP32), leave
95 // ROM_LOG_MODE undefined (no need to call this function during startup)
96 #if CONFIG_BOOT_ROM_LOG_ALWAYS_OFF
97 #define ROM_LOG_MODE ESP_EFUSE_ROM_LOG_ALWAYS_OFF
98 #elif CONFIG_BOOT_ROM_LOG_ON_GPIO_LOW
99 #define ROM_LOG_MODE ESP_EFUSE_ROM_LOG_ON_GPIO_LOW
100 #elif CONFIG_BOOT_ROM_LOG_ON_GPIO_HIGH
101 #define ROM_LOG_MODE ESP_EFUSE_ROM_LOG_ON_GPIO_HIGH
102 #endif
103 
104 
105 uint64_t g_startup_time = 0;
106 
107 #if SOC_APB_BACKUP_DMA
108 // APB DMA lock initialising API
109 extern void esp_apb_backup_dma_lock_init(void);
110 #endif
111 
112 // App entry point for core 0
113 extern void esp_startup_start_app(void);
114 
115 // Entry point for core 0 from hardware init (port layer)
116 void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default"))) __attribute__((noreturn));
117 
118 #if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
119 // Entry point for core [1..X] from hardware init (port layer)
120 void start_cpu_other_cores(void) __attribute__((weak, alias("start_cpu_other_cores_default"))) __attribute__((noreturn));
121 
122 // App entry point for core [1..X]
123 void esp_startup_start_app_other_cores(void) __attribute__((weak, alias("esp_startup_start_app_other_cores_default"))) __attribute__((noreturn));
124 
125 static volatile bool s_system_inited[SOC_CPU_CORES_NUM] = { false };
126 
127 const sys_startup_fn_t g_startup_fn[SOC_CPU_CORES_NUM] = { [0] = start_cpu0,
128 #if SOC_CPU_CORES_NUM > 1
129     [1 ... SOC_CPU_CORES_NUM - 1] = start_cpu_other_cores
130 #endif
131 };
132 
133 static volatile bool s_system_full_inited = false;
134 #else
135 const sys_startup_fn_t g_startup_fn[1] = { start_cpu0 };
136 #endif
137 
138 #ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
139 // workaround for C++ exception crashes
140 void _Unwind_SetNoFunctionContextInstall(unsigned char enable) __attribute__((weak, alias("_Unwind_SetNoFunctionContextInstall_Default")));
141 // workaround for C++ exception large memory allocation
142 void _Unwind_SetEnableExceptionFdeSorting(unsigned char enable);
143 
_Unwind_SetNoFunctionContextInstall_Default(unsigned char enable)144 static IRAM_ATTR void _Unwind_SetNoFunctionContextInstall_Default(unsigned char enable __attribute__((unused)))
145 {
146     (void)0;
147 }
148 #endif // CONFIG_COMPILER_CXX_EXCEPTIONS
149 
150 static const char* TAG = "cpu_start";
151 
152 /**
153  * This function overwrites a the same function of libsupc++ (part of libstdc++).
154  * Consequently, libsupc++ will then follow our configured exception emergency pool size.
155  *
156  * It will be called even with -fno-exception for user code since the stdlib still uses exceptions.
157  */
__cxx_eh_arena_size_get(void)158 size_t __cxx_eh_arena_size_get(void)
159 {
160 #ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
161     return CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE;
162 #else
163     return 0;
164 #endif
165 }
166 
167 /**
168  * Xtensa gcc is configured to emit a .ctors section, RISC-V gcc is configured with --enable-initfini-array
169  * so it emits an .init_array section instead.
170  * But the init_priority sections will be sorted for iteration in ascending order during startup.
171  * The rest of the init_array sections is sorted for iteration in descending order during startup, however.
172  * Hence a different section is generated for the init_priority functions which is looped
173  * over in ascending direction instead of descending direction.
174  * The RISC-V-specific behavior is dependent on the linker script ld/esp32c3/sections.ld.in.
175  */
do_global_ctors(void)176 static void do_global_ctors(void)
177 {
178 #if __riscv
179     extern void (*__init_priority_array_start)(void);
180     extern void (*__init_priority_array_end)(void);
181 #endif
182 
183     extern void (*__init_array_start)(void);
184     extern void (*__init_array_end)(void);
185 
186 #ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
187     struct object { long placeholder[ 10 ]; };
188     void __register_frame_info (const void *begin, struct object *ob);
189     extern char __eh_frame[];
190 
191     static struct object ob;
192     __register_frame_info( __eh_frame, &ob );
193 #endif // CONFIG_COMPILER_CXX_EXCEPTIONS
194 
195     void (**p)(void);
196 
197 #if __riscv
198     for (p = &__init_priority_array_start; p < &__init_priority_array_end; ++p) {
199         ESP_LOGD(TAG, "calling init function: %p", *p);
200         (*p)();
201     }
202 #endif
203 
204     for (p = &__init_array_end - 1; p >= &__init_array_start; --p) {
205         ESP_LOGD(TAG, "calling init function: %p", *p);
206         (*p)();
207     }
208 }
209 
210 /**
211  * @brief Call component init functions defined using ESP_SYSTEM_INIT_Fn macros.
212  * The esp_system_init_fn_t structures describing these functions are collected into
213  * an array [_esp_system_init_fn_array_start, _esp_system_init_fn_array_end) by the
214  * linker. The functions are sorted by their priority value.
215  * The sequence of the init function calls (sorted by priority) is documented in
216  * system_init_fn.txt file.
217  */
do_system_init_fn(void)218 static void do_system_init_fn(void)
219 {
220     extern esp_system_init_fn_t _esp_system_init_fn_array_start;
221     extern esp_system_init_fn_t _esp_system_init_fn_array_end;
222 
223     esp_system_init_fn_t *p;
224 
225     int core_id = esp_cpu_get_core_id();
226     for (p = &_esp_system_init_fn_array_start; p < &_esp_system_init_fn_array_end; ++p) {
227         if (p->cores & BIT(core_id)) {
228             ESP_LOGD(TAG, "calling init function: %p on core: %d", p->fn, core_id);
229             esp_err_t err = (*(p->fn))();
230             if (err != ESP_OK) {
231                 ESP_LOGE(TAG, "init function %p has failed (0x%x), aborting", p->fn, err);
232                 abort();
233             }
234         }
235     }
236 
237 #if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
238     s_system_inited[core_id] = true;
239 #endif
240 }
241 
242 #if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
esp_startup_start_app_other_cores_default(void)243 static void  esp_startup_start_app_other_cores_default(void)
244 {
245     while (1) {
246         esp_rom_delay_us(UINT32_MAX);
247     }
248 }
249 
250 /* This function has to be in IRAM, as while it is running on CPU1, CPU0 may do some flash operations
251  * (e.g. initialize the core dump), which means that cache will be disabled.
252  */
start_cpu_other_cores_default(void)253 static void IRAM_ATTR start_cpu_other_cores_default(void)
254 {
255     do_system_init_fn();
256 
257     while (!s_system_full_inited) {
258         esp_rom_delay_us(100);
259     }
260 
261     esp_startup_start_app_other_cores();
262 }
263 #endif
264 
do_core_init(void)265 static void do_core_init(void)
266 {
267     /* Initialize heap allocator. WARNING: This *needs* to happen *after* the app cpu has booted.
268        If the heap allocator is initialized first, it will put free memory linked list items into
269        memory also used by the ROM. Starting the app cpu will let its ROM initialize that memory,
270        corrupting those linked lists. Initializing the allocator *after* the app cpu has booted
271        works around this problem.
272        With SPI RAM enabled, there's a second reason: half of the SPI RAM will be managed by the
273        app CPU, and when that is not up yet, the memory will be inaccessible and heap_caps_init may
274        fail initializing it properly. */
275     heap_caps_init();
276 
277     // When apptrace module is enabled, there will be SEGGER_SYSVIEW calls in the newlib init.
278     // SEGGER_SYSVIEW relies on apptrace module
279     // apptrace module uses esp_timer_get_time to determine timeout conditions.
280     // esp_timer early initialization is required for esp_timer_get_time to work.
281     esp_timer_early_init();
282     esp_newlib_init();
283 
284 #if CONFIG_SPIRAM_BOOT_INIT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC)
285     if (esp_psram_is_initialized()) {
286         esp_err_t r=esp_psram_extram_add_to_heap_allocator();
287         if (r != ESP_OK) {
288             ESP_EARLY_LOGE(TAG, "External RAM could not be added to heap!");
289             abort();
290         }
291 #if CONFIG_SPIRAM_USE_MALLOC
292         heap_caps_malloc_extmem_enable(CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL);
293 #endif
294     }
295 #endif
296 
297 #if CONFIG_ESP_BROWNOUT_DET
298     // [refactor-todo] leads to call chain rtc_is_register (driver) -> esp_intr_alloc (esp32/esp32s2) ->
299     // malloc (newlib) -> heap_caps_malloc (heap), so heap must be at least initialized
300     esp_brownout_init();
301 #else
302 #if SOC_CAPS_NO_RESET_BY_ANA_BOD
303     brownout_ll_ana_reset_enable(false);
304 #endif // SOC_CAPS_NO_RESET_BY_ANA_BOD
305 #endif // CONFIG_ESP_BROWNOUT_DET
306 
307     esp_newlib_time_init();
308 
309 #if CONFIG_VFS_SUPPORT_IO
310     // VFS console register.
311     esp_err_t vfs_err = esp_vfs_console_register();
312     assert(vfs_err == ESP_OK && "Failed to register vfs console");
313 #endif
314 
315 #if defined(CONFIG_VFS_SUPPORT_IO) && !defined(CONFIG_ESP_CONSOLE_NONE)
316     const static char *default_stdio_dev = "/dev/console/";
317     esp_reent_init(_GLOBAL_REENT);
318     _GLOBAL_REENT->_stdin  = fopen(default_stdio_dev, "r");
319     _GLOBAL_REENT->_stdout = fopen(default_stdio_dev, "w");
320     _GLOBAL_REENT->_stderr = fopen(default_stdio_dev, "w");
321 #if ESP_ROM_NEEDS_SWSETUP_WORKAROUND
322     /*
323     - This workaround for printf functions using 32-bit time_t after the 64-bit time_t upgrade
324     - The 32-bit time_t usage is triggered through ROM Newlib functions printf related functions calling __swsetup_r() on
325       the first call to a particular file pointer (i.e., stdin, stdout, stderr)
326     - Thus, we call the toolchain version of __swsetup_r() now (before any printf calls are made) to setup all of the
327       file pointers. Thus, the ROM newlib code will never call the ROM version of __swsetup_r().
328     - See IDFGH-7728 for more details
329     */
330     extern int __swsetup_r(struct _reent *, FILE *);
331     __swsetup_r(_GLOBAL_REENT, _GLOBAL_REENT->_stdout);
332     __swsetup_r(_GLOBAL_REENT, _GLOBAL_REENT->_stderr);
333     __swsetup_r(_GLOBAL_REENT, _GLOBAL_REENT->_stdin);
334 #endif // ESP_ROM_NEEDS_SWSETUP_WORKAROUND
335 #else // defined(CONFIG_VFS_SUPPORT_IO) && !defined(CONFIG_ESP_CONSOLE_NONE)
336     _REENT_SMALL_CHECK_INIT(_GLOBAL_REENT);
337 #endif // defined(CONFIG_VFS_SUPPORT_IO) && !defined(CONFIG_ESP_CONSOLE_NONE)
338 
339     esp_err_t err __attribute__((unused));
340 
341     err = esp_pthread_init();
342     assert(err == ESP_OK && "Failed to init pthread module!");
343 
344 #if !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP
345 #if CONFIG_SPI_FLASH_ROM_IMPL
346     spi_flash_rom_impl_init();
347 #endif
348 
349     esp_flash_app_init();
350     esp_err_t flash_ret = esp_flash_init_default_chip();
351     assert(flash_ret == ESP_OK);
352     (void)flash_ret;
353 #if CONFIG_SPI_FLASH_BROWNOUT_RESET
354     spi_flash_needs_reset_check();
355 #endif // CONFIG_SPI_FLASH_BROWNOUT_RESET
356 #endif // !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP
357 
358 #ifdef CONFIG_EFUSE_VIRTUAL
359     ESP_LOGW(TAG, "eFuse virtual mode is enabled. If Secure boot or Flash encryption is enabled then it does not provide any security. FOR TESTING ONLY!");
360 #ifdef CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH
361     const esp_partition_t *efuse_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_EFUSE_EM, NULL);
362     if (efuse_partition) {
363         esp_efuse_init_virtual_mode_in_flash(efuse_partition->address, efuse_partition->size);
364     }
365 #endif
366 #endif
367 
368 #if CONFIG_BOOTLOADER_APP_ANTI_ROLLBACK
369     // For anti-rollback case, recheck security version before we boot-up the current application
370     assert(esp_efuse_check_secure_version(esp_app_get_description()->secure_version) == true && "Incorrect secure version of app");
371 #endif
372 
373 #ifdef CONFIG_SECURE_FLASH_ENC_ENABLED
374     esp_flash_encryption_init_checks();
375 #endif
376 
377 #if defined(CONFIG_SECURE_BOOT) || defined(CONFIG_SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT)
378     // Note: in some configs this may read flash, so placed after flash init
379     esp_secure_boot_init_checks();
380 #endif
381 
382 #if SOC_EFUSE_ECDSA_USE_HARDWARE_K
383     if (esp_efuse_find_purpose(ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY, NULL)) {
384         // ECDSA key purpose block is present and hence permanently enable
385         // the hardware TRNG supplied k mode (most secure mode)
386         if (!CONFIG_IDF_TARGET_ESP32H2 || (CONFIG_IDF_TARGET_ESP32H2 && !ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 102))) {
387             err = esp_efuse_write_field_bit(ESP_EFUSE_ECDSA_FORCE_USE_HARDWARE_K);
388             assert(err == ESP_OK && "Failed to enable ECDSA hardware k mode");
389         }
390     }
391 #endif
392 
393 #if CONFIG_SECURE_DISABLE_ROM_DL_MODE
394     err = esp_efuse_disable_rom_download_mode();
395     assert(err == ESP_OK && "Failed to disable ROM download mode");
396 #endif
397 
398 #if CONFIG_SECURE_ENABLE_SECURE_ROM_DL_MODE
399     err = esp_efuse_enable_rom_secure_download_mode();
400     assert(err == ESP_OK && "Failed to enable Secure Download mode");
401 #endif
402 
403 #if CONFIG_ESP32_DISABLE_BASIC_ROM_CONSOLE
404     esp_efuse_disable_basic_rom_console();
405 #endif
406 
407 #ifdef ROM_LOG_MODE
408     esp_efuse_set_rom_log_scheme(ROM_LOG_MODE);
409 #endif
410 
411 #if CONFIG_ESP_XT_WDT
412     esp_xt_wdt_config_t cfg = {
413         .timeout                = CONFIG_ESP_XT_WDT_TIMEOUT,
414         .auto_backup_clk_enable = CONFIG_ESP_XT_WDT_BACKUP_CLK_ENABLE,
415     };
416     err = esp_xt_wdt_init(&cfg);
417     assert(err == ESP_OK && "Failed to init xtwdt");
418 #endif
419 }
420 
do_secondary_init(void)421 static void do_secondary_init(void)
422 {
423 #if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
424     // The port layer transferred control to this function with other cores 'paused',
425     // resume execution so that cores might execute component initialization functions.
426     startup_resume_other_cores();
427 #endif
428 
429     // Execute initialization functions esp_system_init_fn_t assigned to the main core. While
430     // this is happening, all other cores are executing the initialization functions
431     // assigned to them since they have been resumed already.
432     do_system_init_fn();
433 
434 #if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
435     // Wait for all cores to finish secondary init.
436     volatile bool system_inited = false;
437 
438     while (!system_inited) {
439         system_inited = true;
440         for (int i = 0; i < SOC_CPU_CORES_NUM; i++) {
441             system_inited &= s_system_inited[i];
442         }
443         esp_rom_delay_us(100);
444     }
445 #endif
446 }
447 
start_cpu0_default(void)448 static void start_cpu0_default(void)
449 {
450 
451     ESP_EARLY_LOGI(TAG, "Pro cpu start user code");
452     int cpu_freq = esp_clk_cpu_freq();
453     ESP_EARLY_LOGI(TAG, "cpu freq: %d Hz", cpu_freq);
454 
455 #ifdef WITH_APP_IMAGE_INFO
456     // Display information about the current running image.
457     if (LOG_LOCAL_LEVEL >= ESP_LOG_INFO) {
458         const esp_app_desc_t *app_desc = esp_app_get_description();
459         ESP_EARLY_LOGI(TAG, "Application information:");
460 #ifndef CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR
461         ESP_EARLY_LOGI(TAG, "Project name:     %s", app_desc->project_name);
462 #endif
463 #ifndef CONFIG_APP_EXCLUDE_PROJECT_VER_VAR
464         ESP_EARLY_LOGI(TAG, "App version:      %s", app_desc->version);
465 #endif
466 #ifdef CONFIG_BOOTLOADER_APP_SECURE_VERSION
467         ESP_EARLY_LOGI(TAG, "Secure version:   %d", app_desc->secure_version);
468 #endif
469 #ifdef CONFIG_APP_COMPILE_TIME_DATE
470         ESP_EARLY_LOGI(TAG, "Compile time:     %s %s", app_desc->date, app_desc->time);
471 #endif
472         char buf[17];
473         esp_app_get_elf_sha256(buf, sizeof(buf));
474         ESP_EARLY_LOGI(TAG, "ELF file SHA256:  %s...", buf);
475         ESP_EARLY_LOGI(TAG, "ESP-IDF:          %s", app_desc->idf_ver);
476 
477         ESP_EARLY_LOGI(TAG, "Min chip rev:     v%d.%d", CONFIG_ESP_REV_MIN_FULL / 100, CONFIG_ESP_REV_MIN_FULL % 100);
478         ESP_EARLY_LOGI(TAG, "Max chip rev:     v%d.%d %s",CONFIG_ESP_REV_MAX_FULL / 100, CONFIG_ESP_REV_MAX_FULL % 100,
479                        efuse_ll_get_disable_wafer_version_major() ? "(constraint ignored)" : "");
480         unsigned revision = efuse_hal_chip_revision();
481         ESP_EARLY_LOGI(TAG, "Chip rev:         v%d.%d", revision / 100, revision % 100);
482     }
483 #endif
484 
485     // Initialize core components and services.
486     do_core_init();
487 
488     // Execute constructors.
489     do_global_ctors();
490 
491     // Execute init functions of other components; blocks
492     // until all cores finish (when !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE).
493     do_secondary_init();
494 
495     // Now that the application is about to start, disable boot watchdog
496 #ifndef CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE
497     wdt_hal_context_t rtc_wdt_ctx = RWDT_HAL_CONTEXT_DEFAULT();
498     wdt_hal_write_protect_disable(&rtc_wdt_ctx);
499     wdt_hal_disable(&rtc_wdt_ctx);
500     wdt_hal_write_protect_enable(&rtc_wdt_ctx);
501 #endif
502 
503 #if SOC_CPU_CORES_NUM > 1 && !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
504     s_system_full_inited = true;
505 #endif
506 
507     esp_startup_start_app();
508     while (1);
509 }
510 
511 ESP_SYSTEM_INIT_FN(init_components0, BIT(0), 200)
512 {
513 #if CONFIG_ESP_DEBUG_STUBS_ENABLE
514     esp_dbg_stubs_init();
515 #endif
516 
517 #if defined(CONFIG_PM_ENABLE)
518     esp_pm_impl_init();
519 #endif
520 
521 #if CONFIG_ESP_COREDUMP_ENABLE
522     esp_core_dump_init();
523 #endif
524 
525 #if SOC_APB_BACKUP_DMA
526     esp_apb_backup_dma_lock_init();
527 #endif
528 
529 #if CONFIG_SW_COEXIST_ENABLE || CONFIG_EXTERNAL_COEX_ENABLE
530     esp_coex_adapter_register(&g_coex_adapter_funcs);
531     coex_pre_init();
532 #endif
533 
534 #ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
535     ESP_EARLY_LOGD(TAG, "Setting C++ exception workarounds.");
536     _Unwind_SetNoFunctionContextInstall(1);
537     _Unwind_SetEnableExceptionFdeSorting(0);
538 #endif // CONFIG_COMPILER_CXX_EXCEPTIONS
539 
540     return ESP_OK;
541 }
542