bootloader: override the 2nd stage bootloader

Add the possibility to have user bootloader components. This is performed
from an application/project, by creating bootloader components. To do so,
it is required to create a `bootloader_component` directory containing
the custom modules to be compiled with the bootloader.

Thanks to this, two solutions are available to override the bootloader now:
- Using hooks within a user bootloader component
- Using a user defined `main` bootloader component to totally override the
  old implementation

Please check the two new examples in `examples/custom_bootloader`

* Closes https://github.com/espressif/esp-idf/issues/7043
This commit is contained in:
Omar Chebib
2021-04-15 10:31:33 +08:00
parent 82a5f9c4b7
commit a79acb413e
27 changed files with 435 additions and 5 deletions

View File

@@ -0,0 +1,8 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(main)

View File

@@ -0,0 +1,8 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := bootloader_override
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,49 @@
# Bootloader override
(See the README.md file in the upper level for more information about bootloader examples.)
The purpose of this example is to show how to override the second stage bootloader from a regular project.
**NOTE**: Overriding the bootloader is not supported with `Makefile` build system, it is only available with `CMake`.
## How to use example
Simply compile it:
```
idf.py build
```
And flash it with the following commands:
```
idf.py flash
```
This custom bootloader does not do more than the older bootloader, it only prints an extra message on start up:
```
[boot] Custom bootloader has been initialized correctly.
```
## Organisation of this example
This project contains an application, in the `main` directory that represents a user program.
It also contains a `bootloader_components` directory that, as it name states, contains a component that will override the current bootloader implementation.
Below is a short explanation of files in the project folder.
```
├── CMakeLists.txt
├── main
│   ├── CMakeLists.txt
│   └── main.c User application
├── bootloader_components
│   └── main
│   ├── component.mk
│   ├── CMakeLists.txt
│   ├── ld/
│   │ └── ...
│   └── bootloader_start.c Implementation of the second stage bootloader
└── README.md This is the file you are currently reading
```
As stated in the `README.md` file in the upper level, when the bootloader components is named `main`, it overrides
the whole second stage bootloader code.

View File

@@ -0,0 +1,9 @@
idf_component_register(SRCS "bootloader_start.c"
REQUIRES bootloader bootloader_support)
idf_build_get_property(target IDF_TARGET)
# Use the linker script files from the actual bootloader
set(scripts "${IDF_PATH}/components/bootloader/subproject/main/ld/${target}/bootloader.ld"
"${IDF_PATH}/components/bootloader/subproject/main/ld/${target}/bootloader.rom.ld")
target_linker_script(${COMPONENT_LIB} INTERFACE "${scripts}")

View File

@@ -0,0 +1,67 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include "esp_log.h"
#include "bootloader_init.h"
#include "bootloader_utility.h"
#include "bootloader_common.h"
static const char* TAG = "boot";
static int select_partition_number(bootloader_state_t *bs);
/*
* We arrive here after the ROM bootloader finished loading this second stage bootloader from flash.
* The hardware is mostly uninitialized, flash cache is down and the app CPU is in reset.
* We do have a stack, so we can do the initialization in C.
*/
void __attribute__((noreturn)) call_start_cpu0(void)
{
// 1. Hardware initialization
if (bootloader_init() != ESP_OK) {
bootloader_reset();
}
#ifdef CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP
// If this boot is a wake up from the deep sleep then go to the short way,
// try to load the application which worked before deep sleep.
// It skips a lot of checks due to it was done before (while first boot).
bootloader_utility_load_boot_image_from_deep_sleep();
// If it is not successful try to load an application as usual.
#endif
// 2. Select the number of boot partition
bootloader_state_t bs = {0};
int boot_index = select_partition_number(&bs);
if (boot_index == INVALID_INDEX) {
bootloader_reset();
}
// 2.1 Print a custom message!
esp_rom_printf("[%s] Custom bootloader has been initialized correctly.\n", TAG);
// 3. Load the app image for booting
bootloader_utility_load_boot_image(&bs, boot_index);
}
// Select the number of boot partition
static int select_partition_number(bootloader_state_t *bs)
{
// 1. Load partition table
if (!bootloader_utility_load_partition_table(bs)) {
ESP_LOGE(TAG, "load partition table error!");
return INVALID_INDEX;
}
// 2. Select the number of boot partition
return bootloader_utility_get_selected_boot_partition(bs);
}
// Return global reent struct if any newlib functions are linked to bootloader
struct _reent *__getreent(void)
{
return _GLOBAL_REENT;
}

View File

@@ -0,0 +1,20 @@
from __future__ import print_function
import ttfw_idf
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC', target=['esp32', 'esp32s2', 'esp32c3'])
def test_custom_bootloader_impl_example(env, _): # type: ignore
# Test with default build configurations
dut = env.get_dut('main', 'examples/custom_bootloader/bootloader_override')
dut.start_app()
# Expect to read a message from the custom bootloader
dut.expect('Custom bootloader has been initialized correctly.')
# Expect to read a message from the user application
dut.expect('Application started!')
if __name__ == '__main__':
test_custom_bootloader_impl_example()

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "bootloader_override_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
void app_main(void)
{
/**
* Nothing special is done here, everything interesting in this example
* is done in the custom bootloader code, located in:
* `bootloader_components/main/bootloader_start.c`
*/
printf("Application started!\n");
}

View File

@@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)