Awesome, very interesting!
There is a high level overview of the ESP-IDF application startup flow (from ROM code to app) here:
https://docs.espressif.com/projects/esp ... artup-flow
Aside from this, the ESP-IDF source code for the bootloader and app startup is probably the best place to see all the details:
https://github.com/espressif/esp-idf/tr ... oject/main
https://github.com/espressif/esp-idf/tr ... er_support
https://github.com/espressif/esp-idf/bl ... pu_start.c
Some of these things (like the MMU which maps the flash contents into the address space) are also documented in the ESP32 Technical Reference Manual..
Given some of the complex nature of ESP32 compared to many simpler microcontrollers, I'd suggest maybe starting by trying to build an application as a Rust static library and then link it into an existing ESP-IDF project - so it will use the ESP-IDF software bootloader, and then call the ESP-IDF app startup code in cpu_start.c which then calls into the Rust code after the basics are running. There are at least two entry points you could use: app_main() (the C function which most C IDF apps use) runs in a FreeRTOS task already, so FreeRTOS is already running. If you want something lower level, start_cpu0() and start_cpu1() are weak linked functions in IDF which do the RTOS startup after the initial hardware configuration is done - so you could override these with functions that call into Rust immediately.
Once that works, it's a solid base to iterate on. As a second stage, you could expand the Rust part to take over all of the app startup so the ELF for the "app" is written entirely in Rust, but still using the ESP-IDF software bootloader.
And as a third stage if you wanted you could take the ESP-IDF software bootloader out of the equation, and either build a bootloader in Rust or run the app directly from the ROM bootloader.
Alternatively to everything I wrote above, if you want to start from the other end with very simple Rust code (ie running out of IRAM only, no flash cache, etc) running in place of the software bootloader - this is simpler but it leaves you with some constraints (mostly, no flash cache mappings yet so you're limited to the code which fits in IRAM). So it's a longer journey before you have a lot of functionality on the Rust side. The software bootloader entry point is basically a C function - you have a stack already, etc - because the ROM code does some setup and loads the software bootloader from flash into IRAM for you. The software bootloader uses the same CPU exception vectors as the ROM (ie stubs, basically) so there isn't an example of those in the bootloader project, but you can copy the IDF exception vectors as referenced from the startup code in IDF (vectors here
, linker script that lays them out at correct offsets is here
, moving the vector base pointer is done like this
.) You could also take a look at the Zephyr or NuttX projects who have roughly that same level of functionality at the moment (ie run from IRAM only, but basics are in place).
If you have specific questions, feel free to ask.