xtensa/esp32: Let a protected build boot from simple boot - #19789
Merged
xiaoxiang781216 merged 1 commit intoAug 11, 2026
Conversation
A protected build on the ESP32 could only use the legacy IDF image format. Kconfig allowed simple boot to be selected with BUILD_PROTECTED, because the legacy format is only a default and not a select, but the result did not link and then did not boot. Simple boot has no second-stage bootloader. __start() maps the flash itself, so everything it reaches must already be in RAM. kernel-space.ld pinned none of it, and it did not place esp32_start at all, so the entry point went to the flash the code was about to map. The chip loaded the RAM segments, jumped to 0x400d0ba4 and took an IllegalInstruction on the first instruction. So this pins the bootloader, flash, ROM, clock and log objects that bootloader_init() and map_rom_segments() reach, along with esp32_start itself, and defines the six _image_* symbols that __start() needs. All of it is behind CONFIG_ESPRESSIF_SIMPLE_BOOT, so a legacy build gets the same IRAM it had before. kernel-space.ld also had no `#include <nuttx/config.h>'. It held no conditionals until now, so nothing showed the omission: the new blocks compiled away silently and the link failed as if the file had not been changed. The default is unchanged. A protected build still selects the legacy format unless the user clears CONFIG_ESP32_APP_FORMAT_LEGACY. Verified on an ESP32-DevKitC V4, ESP32-D0WD-V3 revision 3.1, with esp32-devkitc:knsh and the legacy format turned off. The kernel flashes at 0x1000 and the user image at 0x90000, with no bootloader and no partition table. It maps seven segments, reaches NSH, and runs ostest to the same point as the legacy build. Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
xiaoxiang781216
approved these changes
Aug 11, 2026
acassis
approved these changes
Aug 11, 2026
Contributor
|
Hi @casaroli , thanks for the contribution. If you don't mind, I'd suggest removing the legacy bootloader from any |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A protected build on the ESP32 could only use the legacy IDF image format. This pull request lets it boot from simple boot instead.
Kconfig already allowed the choice.
ESP32_APP_FORMAT_LEGACYis only adefault y if BUILD_PROTECTED, not aselect, so a user could clear it and getCONFIG_ESPRESSIF_SIMPLE_BOOT=y. The result did not link, and after it linked it did not boot.Simple boot has no second-stage bootloader.
__start()maps the flash itself, so everything it reaches has to be in RAM already.kernel-space.ldpinned none of it, and it did not placeesp32_startat all, so the entry point landed in the flash that the code was about to map:What changed
One file,
boards/xtensa/esp32/common/scripts/kernel-space.ld, and every block is behindCONFIG_ESPRESSIF_SIMPLE_BOOT. A legacy build gets exactly the IRAM it had before.It pins
esp32_startand the bootloader, flash, ROM, clock and log objects thatbootloader_init()andmap_rom_segments()reach, with the matching read-only data.It defines the six
_image_irom_*and_image_drom_*symbols that__start()needs to link. Under simple bootmap_rom_segments()reads the segment table out of the image in flash, so these only have to exist.It adds
#include <nuttx/config.h>. The file held no conditionals until now, so nothing revealed the omission. Without it the new blocks compiled away in silence and the link failed exactly as if the file had never been edited. This cost me a build cycle and is worth knowing about for anyone adding a conditional to this script.The default does not change. A protected build still uses the legacy format unless the user clears
CONFIG_ESP32_APP_FORMAT_LEGACY.This is the ESP32 counterpart of #19764, which did the same for the ESP32-S3.
Testing
Board: ESP32-DevKitC V4, with an ESP32-D0WD-V3 revision 3.1.
Host: macOS 15 on Apple Silicon,
xtensa-esp32-elf-gcc12.2.0.esp32-devkitc:knshesp32-devkitc:knshWith simple boot the kernel flashes at
0x1000and the user image at0x90000. There is no bootloader and no partition table to flash:ostestreaches the same point in both image formats, so userspace behaves the same either way.Note for reviewers:
ostestdoes not finish onesp32-devkitc:knshin either format. It stops in the barrier test withbarrier_test: ERROR thread 6 create, status=12. That is heap size, not this change — the protected user heap arena is 95,908 B against 320,236 B in the flat build, and eight barrier threads want 8192 B of stack each. I confirmed it is pre-existing by buildingesp32-devkitc:knshfrommasterand getting the identical failure on the same board.tools/checkpatch.sh -c -u -m -greports no errors.