mirror of
https://github.com/espressif/ESP8266_RTOS_SDK.git
synced 2025-05-24 02:28:21 +08:00
feat(bootloader_support): add bootloader support from idf
idf commit: a3a0b01c
This commit is contained in:
7
components/bootloader_support/Makefile.projbuild
Normal file
7
components/bootloader_support/Makefile.projbuild
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
$(SECURE_BOOT_SIGNING_KEY):
|
||||||
|
@echo "Need to generate secure boot signing key."
|
||||||
|
@echo "One way is to run this command:"
|
||||||
|
@echo "$(ESPSECUREPY) generate_signing_key $@"
|
||||||
|
@echo "Keep key file safe after generating."
|
||||||
|
@echo "(See secure boot documentation for risks & alternatives.)"
|
||||||
|
@exit 1
|
9
components/bootloader_support/README.rst
Normal file
9
components/bootloader_support/README.rst
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
Bootloader Support Component
|
||||||
|
============================
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
|
||||||
|
"Bootloader support" contains APIs which are used by the bootloader but are also needed for the main app.
|
||||||
|
|
||||||
|
Code in this component needs to be aware of being executed in a bootloader environment (no RTOS available, BOOTLOADER_BUILD macro set) or in an esp-idf app environment (RTOS running, need locking support.)
|
45
components/bootloader_support/component.mk
Normal file
45
components/bootloader_support/component.mk
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
COMPONENT_ADD_INCLUDEDIRS := include
|
||||||
|
COMPONENT_PRIV_INCLUDEDIRS := include_priv
|
||||||
|
|
||||||
|
ifdef IS_BOOTLOADER_BUILD
|
||||||
|
# share "private" headers with the bootloader component
|
||||||
|
# eventual goal: all functionality that needs this lives in bootloader_support
|
||||||
|
COMPONENT_ADD_INCLUDEDIRS += include_priv
|
||||||
|
endif
|
||||||
|
|
||||||
|
COMPONENT_SRCDIRS := src
|
||||||
|
|
||||||
|
#
|
||||||
|
# Secure boot signing key support
|
||||||
|
#
|
||||||
|
ifdef CONFIG_SECURE_BOOT_ENABLED
|
||||||
|
|
||||||
|
# this path is created relative to the component build directory
|
||||||
|
SECURE_BOOT_VERIFICATION_KEY := $(abspath signature_verification_key.bin)
|
||||||
|
|
||||||
|
ifdef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES
|
||||||
|
# verification key derived from signing key.
|
||||||
|
$(SECURE_BOOT_VERIFICATION_KEY): $(SECURE_BOOT_SIGNING_KEY) $(SDKCONFIG_MAKEFILE)
|
||||||
|
$(ESPSECUREPY) extract_public_key --keyfile $< $@
|
||||||
|
else
|
||||||
|
# find the configured public key file
|
||||||
|
ORIG_SECURE_BOOT_VERIFICATION_KEY := $(call resolvepath,$(call dequote,$(CONFIG_SECURE_BOOT_VERIFICATION_KEY)),$(PROJECT_PATH))
|
||||||
|
|
||||||
|
$(ORIG_SECURE_BOOT_VERIFICATION_KEY):
|
||||||
|
@echo "Secure boot verification public key '$@' missing."
|
||||||
|
@echo "This can be extracted from the private signing key, see"
|
||||||
|
@echo "docs/security/secure-boot.rst for details."
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
# copy it into the build dir, so the secure boot verification key has
|
||||||
|
# a predictable file name
|
||||||
|
$(SECURE_BOOT_VERIFICATION_KEY): $(ORIG_SECURE_BOOT_VERIFICATION_KEY) $(SDKCONFIG_MAKEFILE)
|
||||||
|
$(summary) CP $< $@
|
||||||
|
cp $< $@
|
||||||
|
endif
|
||||||
|
|
||||||
|
COMPONENT_EXTRA_CLEAN += $(SECURE_BOOT_VERIFICATION_KEY)
|
||||||
|
|
||||||
|
COMPONENT_EMBED_FILES := $(SECURE_BOOT_VERIFICATION_KEY)
|
||||||
|
|
||||||
|
endif
|
21
components/bootloader_support/include/bootloader_clock.h
Normal file
21
components/bootloader_support/include/bootloader_clock.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/** @brief Configure clocks for early boot
|
||||||
|
*
|
||||||
|
* Called by bootloader, or by the app if the bootloader version is old (pre v2.1).
|
||||||
|
*/
|
||||||
|
void bootloader_clock_configure(void);
|
70
components/bootloader_support/include/bootloader_common.h
Normal file
70
components/bootloader_support/include/bootloader_common.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "esp_flash_data_types.h"
|
||||||
|
|
||||||
|
/// Type of hold a GPIO in low state
|
||||||
|
typedef enum {
|
||||||
|
GPIO_LONG_HOLD = 1, /*!< The long hold GPIO */
|
||||||
|
GPIO_SHORT_HOLD = -1, /*!< The short hold GPIO */
|
||||||
|
GPIO_NOT_HOLD = 0 /*!< If the GPIO input is not low */
|
||||||
|
} esp_comm_gpio_hold_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculate crc for the OTA data partition.
|
||||||
|
*
|
||||||
|
* @param[in] ota_data The OTA data partition.
|
||||||
|
* @return Returns crc value.
|
||||||
|
*/
|
||||||
|
uint32_t bootloader_common_ota_select_crc(const esp_ota_select_entry_t *s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verifies the validity of the OTA data partition
|
||||||
|
*
|
||||||
|
* @param[in] ota_data The OTA data partition.
|
||||||
|
* @return Returns true on valid, false otherwise.
|
||||||
|
*/
|
||||||
|
bool bootloader_common_ota_select_valid(const esp_ota_select_entry_t *s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the GPIO input is a long hold or a short hold.
|
||||||
|
*
|
||||||
|
* Number of the GPIO input will be configured as an input with internal pull-up enabled.
|
||||||
|
* If the GPIO input is held low continuously for delay_sec period then it is a long hold.
|
||||||
|
* If the GPIO input is held low for less period then it is a short hold.
|
||||||
|
*
|
||||||
|
* @param[in] num_pin Number of the GPIO input.
|
||||||
|
* @param[in] delay_sec Input must be driven low for at least this long, continuously.
|
||||||
|
* @return esp_comm_gpio_hold_t Defines type of hold a GPIO in low state.
|
||||||
|
*/
|
||||||
|
esp_comm_gpio_hold_t bootloader_common_check_long_hold_gpio(uint32_t num_pin, uint32_t delay_sec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Erase the partition data that is specified in the transferred list.
|
||||||
|
*
|
||||||
|
* @param[in] list_erase String containing a list of cleared partitions. Like this "nvs, phy". The string must be null-terminal.
|
||||||
|
* @param[in] ota_data_erase If true then the OTA data partition will be cleared (if there is it in partition table).
|
||||||
|
* @return Returns true on success, false otherwise.
|
||||||
|
*/
|
||||||
|
bool bootloader_common_erase_part_type_data(const char *list_erase, bool ota_data_erase);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Determines if the list contains the label
|
||||||
|
*
|
||||||
|
* @param[in] list A string of names delimited by commas or spaces. Like this "nvs, phy, data". The string must be null-terminated.
|
||||||
|
* @param[in] label The substring that will be searched in the list.
|
||||||
|
* @return Returns true if the list contains the label, false otherwise.
|
||||||
|
*/
|
||||||
|
bool bootloader_common_label_search(const char *list, char *label);
|
66
components/bootloader_support/include/esp_efuse.h
Normal file
66
components/bootloader_support/include/esp_efuse.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#ifndef _ESP_EFUSE_H
|
||||||
|
#define _ESP_EFUSE_H
|
||||||
|
|
||||||
|
#include "soc/efuse_reg.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* @brief Permanently update values written to the efuse write registers
|
||||||
|
*
|
||||||
|
* After updating EFUSE_BLKx_WDATAx_REG registers with new values to
|
||||||
|
* write, call this function to permanently write them to efuse.
|
||||||
|
*
|
||||||
|
* @note Setting bits in efuse is permanent, they cannot be unset.
|
||||||
|
*
|
||||||
|
* @note Due to this restriction you don't need to copy values to
|
||||||
|
* Efuse write registers from the matching read registers, bits which
|
||||||
|
* are set in the read register but unset in the matching write
|
||||||
|
* register will be unchanged when new values are burned.
|
||||||
|
*
|
||||||
|
* @note This function is not threadsafe, if calling code updates
|
||||||
|
* efuse values from multiple tasks then this is caller's
|
||||||
|
* responsibility to serialise.
|
||||||
|
*
|
||||||
|
* After burning new efuses, the read registers are updated to match
|
||||||
|
* the new efuse values.
|
||||||
|
*/
|
||||||
|
void esp_efuse_burn_new_values(void);
|
||||||
|
|
||||||
|
/* @brief Reset efuse write registers
|
||||||
|
*
|
||||||
|
* Efuse write registers are written to zero, to negate
|
||||||
|
* any changes that have been staged here.
|
||||||
|
*/
|
||||||
|
void esp_efuse_reset(void);
|
||||||
|
|
||||||
|
/* @brief Disable BASIC ROM Console via efuse
|
||||||
|
*
|
||||||
|
* By default, if booting from flash fails the ESP32 will boot a
|
||||||
|
* BASIC console in ROM.
|
||||||
|
*
|
||||||
|
* Call this function (from bootloader or app) to permanently
|
||||||
|
* disable the console on this chip.
|
||||||
|
*/
|
||||||
|
void esp_efuse_disable_basic_rom_console(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __ESP_EFUSE_H */
|
||||||
|
|
102
components/bootloader_support/include/esp_flash_encrypt.h
Normal file
102
components/bootloader_support/include/esp_flash_encrypt.h
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#ifndef __ESP32_FLASH_ENCRYPT_H
|
||||||
|
#define __ESP32_FLASH_ENCRYPT_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "esp_attr.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_spi_flash.h"
|
||||||
|
#include "soc/efuse_reg.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file esp_partition.h
|
||||||
|
* @brief Support functions for flash encryption features
|
||||||
|
*
|
||||||
|
* Can be compiled as part of app or bootloader code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @brief Is flash encryption currently enabled in hardware?
|
||||||
|
*
|
||||||
|
* Flash encryption is enabled if the FLASH_CRYPT_CNT efuse has an odd number of bits set.
|
||||||
|
*
|
||||||
|
* @return true if flash encryption is enabled.
|
||||||
|
*/
|
||||||
|
static inline /** @cond */ IRAM_ATTR /** @endcond */ bool esp_flash_encryption_enabled(void) {
|
||||||
|
uint32_t flash_crypt_cnt = REG_GET_FIELD(EFUSE_BLK0_RDATA0_REG, EFUSE_RD_FLASH_CRYPT_CNT);
|
||||||
|
/* __builtin_parity is in flash, so we calculate parity inline */
|
||||||
|
bool enabled = false;
|
||||||
|
while(flash_crypt_cnt) {
|
||||||
|
if (flash_crypt_cnt & 1) {
|
||||||
|
enabled = !enabled;
|
||||||
|
}
|
||||||
|
flash_crypt_cnt >>= 1;
|
||||||
|
}
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @brief Update on-device flash encryption
|
||||||
|
*
|
||||||
|
* Intended to be called as part of the bootloader process if flash
|
||||||
|
* encryption is enabled in device menuconfig.
|
||||||
|
*
|
||||||
|
* If FLASH_CRYPT_CNT efuse parity is 1 (ie odd number of bits set),
|
||||||
|
* then return ESP_OK immediately (indicating flash encryption is enabled
|
||||||
|
* and functional).
|
||||||
|
*
|
||||||
|
* If FLASH_CRYPT_CNT efuse parity is 0 (ie even number of bits set),
|
||||||
|
* assume the flash has just been written with plaintext that needs encrypting.
|
||||||
|
*
|
||||||
|
* The following regions of flash are encrypted in place:
|
||||||
|
*
|
||||||
|
* - The bootloader image, if a valid plaintext image is found.[*]
|
||||||
|
* - The partition table, if a valid plaintext table is found.
|
||||||
|
* - Any app partition that contains a valid plaintext app image.
|
||||||
|
* - Any other partitions with the "encrypt" flag set. [**]
|
||||||
|
*
|
||||||
|
* After the re-encryption process completes, a '1' bit is added to the
|
||||||
|
* FLASH_CRYPT_CNT value (setting the parity to 1) and the EFUSE is re-burned.
|
||||||
|
*
|
||||||
|
* [*] If reflashing bootloader with secure boot enabled, pre-encrypt
|
||||||
|
* the bootloader before writing it to flash or secure boot will fail.
|
||||||
|
*
|
||||||
|
* [**] For this reason, if serial re-flashing a previous flashed
|
||||||
|
* device with secure boot enabled and using FLASH_CRYPT_CNT to
|
||||||
|
* trigger re-encryption, you must simultaneously re-flash plaintext
|
||||||
|
* content to all partitions with the "encrypt" flag set or this
|
||||||
|
* data will be corrupted (encrypted twice).
|
||||||
|
*
|
||||||
|
* @note The post-condition of this function is that all
|
||||||
|
* partitions that should be encrypted are encrypted.
|
||||||
|
*
|
||||||
|
* @note Take care not to power off the device while this function
|
||||||
|
* is running, or the partition currently being encrypted will be lost.
|
||||||
|
*
|
||||||
|
* @return ESP_OK if all operations succeeded, ESP_ERR_INVALID_STATE
|
||||||
|
* if a fatal error occured during encryption of all partitions.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_flash_encrypt_check_and_update(void);
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief Encrypt-in-place a block of flash sectors
|
||||||
|
*
|
||||||
|
* @param src_addr Source offset in flash. Should be multiple of 4096 bytes.
|
||||||
|
* @param data_length Length of data to encrypt in bytes. Will be rounded up to next multiple of 4096 bytes.
|
||||||
|
*
|
||||||
|
* @return ESP_OK if all operations succeeded, ESP_ERR_FLASH_OP_FAIL
|
||||||
|
* if SPI flash fails, ESP_ERR_FLASH_OP_TIMEOUT if flash times out.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_flash_encrypt_region(uint32_t src_addr, size_t data_length);
|
||||||
|
|
||||||
|
#endif
|
40
components/bootloader_support/include/esp_flash_partitions.h
Normal file
40
components/bootloader_support/include/esp_flash_partitions.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#ifndef __ESP_FLASH_PARTITIONS_H
|
||||||
|
#define __ESP_FLASH_PARTITIONS_H
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_flash_data_types.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/* Pre-partition table fixed flash offsets */
|
||||||
|
#define ESP_BOOTLOADER_DIGEST_OFFSET 0x0
|
||||||
|
#define ESP_BOOTLOADER_OFFSET 0x1000 /* Offset of bootloader image. Has matching value in bootloader KConfig.projbuild file. */
|
||||||
|
#define ESP_BOOTLOADER_SIZE (ESP_PARTITION_TABLE_OFFSET - ESP_BOOTLOADER_OFFSET)
|
||||||
|
#define ESP_PARTITION_TABLE_OFFSET 0x8000 /* Offset of partition table. Has matching value in partition_table Kconfig.projbuild file. */
|
||||||
|
|
||||||
|
#define ESP_PARTITION_TABLE_MAX_LEN 0xC00 /* Maximum length of partition table data */
|
||||||
|
#define ESP_PARTITION_TABLE_MAX_ENTRIES (ESP_PARTITION_TABLE_MAX_LEN / sizeof(esp_partition_info_t)) /* Maximum length of partition table data, including terminating entry */
|
||||||
|
|
||||||
|
/* @brief Verify the partition table (does not include verifying secure boot cryptographic signature)
|
||||||
|
*
|
||||||
|
* @param partition_table Pointer to at least ESP_PARTITION_TABLE_MAX_ENTRIES of potential partition table data. (ESP_PARTITION_TABLE_MAX_LEN bytes.)
|
||||||
|
* @param log_errors Log errors if the partition table is invalid.
|
||||||
|
* @param num_partitions If result is ESP_OK, num_partitions is updated with total number of partitions (not including terminating entry).
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success, ESP_ERR_INVALID_STATE if partition table is not valid.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_partition_table_basic_verify(const esp_partition_info_t *partition_table, bool log_errors, int *num_partitions);
|
||||||
|
|
||||||
|
#endif
|
152
components/bootloader_support/include/esp_image_format.h
Normal file
152
components/bootloader_support/include/esp_image_format.h
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include "esp_flash_partitions.h"
|
||||||
|
|
||||||
|
#define ESP_ERR_IMAGE_BASE 0x2000
|
||||||
|
#define ESP_ERR_IMAGE_FLASH_FAIL (ESP_ERR_IMAGE_BASE + 1)
|
||||||
|
#define ESP_ERR_IMAGE_INVALID (ESP_ERR_IMAGE_BASE + 2)
|
||||||
|
|
||||||
|
/* Support for app/bootloader image parsing
|
||||||
|
Can be compiled as part of app or bootloader code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* SPI flash mode, used in esp_image_header_t */
|
||||||
|
typedef enum {
|
||||||
|
ESP_IMAGE_SPI_MODE_QIO,
|
||||||
|
ESP_IMAGE_SPI_MODE_QOUT,
|
||||||
|
ESP_IMAGE_SPI_MODE_DIO,
|
||||||
|
ESP_IMAGE_SPI_MODE_DOUT,
|
||||||
|
ESP_IMAGE_SPI_MODE_FAST_READ,
|
||||||
|
ESP_IMAGE_SPI_MODE_SLOW_READ
|
||||||
|
} esp_image_spi_mode_t;
|
||||||
|
|
||||||
|
/* SPI flash clock frequency */
|
||||||
|
enum {
|
||||||
|
ESP_IMAGE_SPI_SPEED_40M,
|
||||||
|
ESP_IMAGE_SPI_SPEED_26M,
|
||||||
|
ESP_IMAGE_SPI_SPEED_20M,
|
||||||
|
ESP_IMAGE_SPI_SPEED_80M = 0xF
|
||||||
|
} esp_image_spi_freq_t;
|
||||||
|
|
||||||
|
/* Supported SPI flash sizes */
|
||||||
|
typedef enum {
|
||||||
|
ESP_IMAGE_FLASH_SIZE_1MB = 0,
|
||||||
|
ESP_IMAGE_FLASH_SIZE_2MB,
|
||||||
|
ESP_IMAGE_FLASH_SIZE_4MB,
|
||||||
|
ESP_IMAGE_FLASH_SIZE_8MB,
|
||||||
|
ESP_IMAGE_FLASH_SIZE_16MB,
|
||||||
|
ESP_IMAGE_FLASH_SIZE_MAX
|
||||||
|
} esp_image_flash_size_t;
|
||||||
|
|
||||||
|
#define ESP_IMAGE_HEADER_MAGIC 0xE9
|
||||||
|
|
||||||
|
/* Main header of binary image */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t magic;
|
||||||
|
uint8_t segment_count;
|
||||||
|
/* flash read mode (esp_image_spi_mode_t as uint8_t) */
|
||||||
|
uint8_t spi_mode;
|
||||||
|
/* flash frequency (esp_image_spi_freq_t as uint8_t) */
|
||||||
|
uint8_t spi_speed: 4;
|
||||||
|
/* flash chip size (esp_image_flash_size_t as uint8_t) */
|
||||||
|
uint8_t spi_size: 4;
|
||||||
|
uint32_t entry_addr;
|
||||||
|
/* WP pin when SPI pins set via efuse (read by ROM bootloader, the IDF bootloader uses software to configure the WP
|
||||||
|
* pin and sets this field to 0xEE=disabled) */
|
||||||
|
uint8_t wp_pin;
|
||||||
|
/* Drive settings for the SPI flash pins (read by ROM bootloader) */
|
||||||
|
uint8_t spi_pin_drv[3];
|
||||||
|
/* Reserved bytes in ESP32 additional header space, currently unused */
|
||||||
|
uint8_t reserved[11];
|
||||||
|
/* If 1, a SHA256 digest "simple hash" (of the entire image) is appended after the checksum. Included in image length. This digest
|
||||||
|
* is separate to secure boot and only used for detecting corruption. For secure boot signed images, the signature
|
||||||
|
* is appended after this (and the simple hash is included in the signed data). */
|
||||||
|
uint8_t hash_appended;
|
||||||
|
} __attribute__((packed)) esp_image_header_t;
|
||||||
|
|
||||||
|
_Static_assert(sizeof(esp_image_header_t) == 24, "binary image header should be 24 bytes");
|
||||||
|
|
||||||
|
/* Header of binary image segment */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t load_addr;
|
||||||
|
uint32_t data_len;
|
||||||
|
} esp_image_segment_header_t;
|
||||||
|
|
||||||
|
#define ESP_IMAGE_MAX_SEGMENTS 16
|
||||||
|
|
||||||
|
/* Structure to hold on-flash image metadata */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t start_addr; /* Start address of image */
|
||||||
|
esp_image_header_t image; /* Header for entire image */
|
||||||
|
esp_image_segment_header_t segments[ESP_IMAGE_MAX_SEGMENTS]; /* Per-segment header data */
|
||||||
|
uint32_t segment_data[ESP_IMAGE_MAX_SEGMENTS]; /* Data offsets for each segment */
|
||||||
|
uint32_t image_len; /* Length of image on flash, in bytes */
|
||||||
|
} esp_image_metadata_t;
|
||||||
|
|
||||||
|
/* Mode selection for esp_image_load() */
|
||||||
|
typedef enum {
|
||||||
|
ESP_IMAGE_VERIFY, /* Verify image contents, load metadata. Print errorsors. */
|
||||||
|
ESP_IMAGE_VERIFY_SILENT, /* Verify image contents, load metadata. Don't print errors. */
|
||||||
|
#ifdef BOOTLOADER_BUILD
|
||||||
|
ESP_IMAGE_LOAD, /* Verify image contents, load to memory. Print errors. */
|
||||||
|
#endif
|
||||||
|
} esp_image_load_mode_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verify and (optionally, in bootloader mode) load an app image.
|
||||||
|
*
|
||||||
|
* If encryption is enabled, data will be transparently decrypted.
|
||||||
|
*
|
||||||
|
* @param mode Mode of operation (verify, silent verify, or load).
|
||||||
|
* @param part Partition to load the app from.
|
||||||
|
* @param[inout] data Pointer to the image metadata structure which is be filled in by this function. 'start_addr' member should be set (to the start address of the image.) Other fields will all be initialised by this function.
|
||||||
|
*
|
||||||
|
* Image validation checks:
|
||||||
|
* - Magic byte.
|
||||||
|
* - Partition smaller than 16MB.
|
||||||
|
* - All segments & image fit in partition.
|
||||||
|
* - 8 bit image checksum is valid.
|
||||||
|
* - SHA-256 of image is valid (if image has this appended).
|
||||||
|
* - (Signature) if signature verification is enabled.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK if verify or load was successful
|
||||||
|
* - ESP_ERR_IMAGE_FLASH_FAIL if a SPI flash error occurs
|
||||||
|
* - ESP_ERR_IMAGE_INVALID if the image appears invalid.
|
||||||
|
* - ESP_ERR_INVALID_ARG if the partition or data pointers are invalid.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Verify the bootloader image.
|
||||||
|
*
|
||||||
|
* @param[out] If result is ESP_OK and this pointer is non-NULL, it
|
||||||
|
* will be set to the length of the bootloader image.
|
||||||
|
*
|
||||||
|
* @return As per esp_image_load_metadata().
|
||||||
|
*/
|
||||||
|
esp_err_t esp_image_verify_bootloader(uint32_t *length);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t drom_addr;
|
||||||
|
uint32_t drom_load_addr;
|
||||||
|
uint32_t drom_size;
|
||||||
|
uint32_t irom_addr;
|
||||||
|
uint32_t irom_load_addr;
|
||||||
|
uint32_t irom_size;
|
||||||
|
} esp_image_flash_mapping_t;
|
107
components/bootloader_support/include/esp_secure_boot.h
Normal file
107
components/bootloader_support/include/esp_secure_boot.h
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include "soc/efuse_reg.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Support functions for secure boot features.
|
||||||
|
|
||||||
|
Can be compiled as part of app or bootloader code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @brief Is secure boot currently enabled in hardware?
|
||||||
|
*
|
||||||
|
* Secure boot is enabled if the ABS_DONE_0 efuse is blown. This means
|
||||||
|
* that the ROM bootloader code will only boot a verified secure
|
||||||
|
* bootloader digest from now on.
|
||||||
|
*
|
||||||
|
* @return true if secure boot is enabled.
|
||||||
|
*/
|
||||||
|
static inline bool esp_secure_boot_enabled(void) {
|
||||||
|
return REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief Enable secure boot if it is not already enabled.
|
||||||
|
*
|
||||||
|
* @important If this function succeeds, secure boot is permanently
|
||||||
|
* enabled on the chip via efuse.
|
||||||
|
*
|
||||||
|
* @important This function is intended to be called from bootloader code only.
|
||||||
|
*
|
||||||
|
* If secure boot is not yet enabled for bootloader, this will
|
||||||
|
* generate the secure boot digest and enable secure boot by blowing
|
||||||
|
* the EFUSE_RD_ABS_DONE_0 efuse.
|
||||||
|
*
|
||||||
|
* This function does not verify secure boot of the bootloader (the
|
||||||
|
* ROM bootloader does this.)
|
||||||
|
*
|
||||||
|
* Will fail if efuses have been part-burned in a way that indicates
|
||||||
|
* secure boot should not or could not be correctly enabled.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return ESP_ERR_INVALID_STATE if efuse state doesn't allow
|
||||||
|
* secure boot to be enabled cleanly. ESP_OK if secure boot
|
||||||
|
* is enabled on this chip from now on.
|
||||||
|
*/
|
||||||
|
esp_err_t esp_secure_boot_permanently_enable(void);
|
||||||
|
|
||||||
|
/** @brief Verify the secure boot signature (determinstic ECDSA w/ SHA256) appended to some binary data in flash.
|
||||||
|
*
|
||||||
|
* Public key is compiled into the calling program. See docs/security/secure-boot.rst for details.
|
||||||
|
*
|
||||||
|
* @param src_addr Starting offset of the data in flash.
|
||||||
|
* @param length Length of data in bytes. Signature is appended -after- length bytes.
|
||||||
|
*
|
||||||
|
* If flash encryption is enabled, the image will be transparently decrypted while being verified.
|
||||||
|
*
|
||||||
|
* @return ESP_OK if signature is valid, ESP_ERR_INVALID_STATE if
|
||||||
|
* signature fails, ESP_FAIL for other failures (ie can't read flash).
|
||||||
|
*/
|
||||||
|
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length);
|
||||||
|
|
||||||
|
/** @brief Verify the secure boot signature block (deterministic ECDSA w/ SHA256) based on the SHA256 hash of some data.
|
||||||
|
*
|
||||||
|
* Similar to esp_secure_boot_verify_signature(), but can be used when the digest is precalculated.
|
||||||
|
* @param sig_block Pointer to signature block data
|
||||||
|
* @param image_digest Pointer to 32 byte buffer holding SHA-256 hash.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @brief Secure boot verification block, on-flash data format. */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t version;
|
||||||
|
uint8_t signature[64];
|
||||||
|
} esp_secure_boot_sig_block_t;
|
||||||
|
|
||||||
|
esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest);
|
||||||
|
|
||||||
|
#define FLASH_OFFS_SECURE_BOOT_IV_DIGEST 0
|
||||||
|
|
||||||
|
/** @brief Secure boot IV+digest header */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t iv[128];
|
||||||
|
uint8_t digest[64];
|
||||||
|
} esp_secure_boot_iv_digest_t;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,54 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#ifndef __BOOT_CONFIG_H__
|
||||||
|
#define __BOOT_CONFIG_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "esp_flash_data_types.h"
|
||||||
|
#include "soc/soc.h"
|
||||||
|
|
||||||
|
#define SPI_SEC_SIZE 0x1000
|
||||||
|
|
||||||
|
#define SPI_ERROR_LOG "spi flash error"
|
||||||
|
|
||||||
|
#define MAX_OTA_SLOTS 16
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
esp_partition_pos_t ota_info;
|
||||||
|
esp_partition_pos_t factory;
|
||||||
|
esp_partition_pos_t test;
|
||||||
|
esp_partition_pos_t ota[MAX_OTA_SLOTS];
|
||||||
|
uint32_t app_count;
|
||||||
|
uint32_t selected_subtype;
|
||||||
|
} bootloader_state_t;
|
||||||
|
|
||||||
|
bool flash_encrypt(bootloader_state_t *bs);
|
||||||
|
|
||||||
|
/* Indices used by index_to_partition are the OTA index
|
||||||
|
number, or these special constants */
|
||||||
|
#define FACTORY_INDEX (-1)
|
||||||
|
#define TEST_APP_INDEX (-2)
|
||||||
|
#define INVALID_INDEX (-99)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __BOOT_CONFIG_H__ */
|
103
components/bootloader_support/include_priv/bootloader_flash.h
Normal file
103
components/bootloader_support/include_priv/bootloader_flash.h
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#ifndef __BOOTLOADER_FLASH_H
|
||||||
|
#define __BOOTLOADER_FLASH_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include "esp_spi_flash.h"
|
||||||
|
|
||||||
|
#define FLASH_SECTOR_SIZE 0x1000
|
||||||
|
|
||||||
|
/* Provide a Flash API for bootloader_support code,
|
||||||
|
that can be used from bootloader or app code.
|
||||||
|
|
||||||
|
This header is available to source code in the bootloader &
|
||||||
|
bootloader_support components only.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Map a region of flash to data memory
|
||||||
|
*
|
||||||
|
* @important In bootloader code, only one region can be bootloader_mmaped at once. The previous region must be bootloader_munmapped before another region is mapped.
|
||||||
|
*
|
||||||
|
* @important In app code, these functions are not thread safe.
|
||||||
|
*
|
||||||
|
* Call bootloader_munmap once for each successful call to bootloader_mmap.
|
||||||
|
*
|
||||||
|
* In esp-idf app, this function maps directly to spi_flash_mmap.
|
||||||
|
*
|
||||||
|
* @param offset - Starting flash offset to map to memory.
|
||||||
|
* @param length - Length of data to map.
|
||||||
|
*
|
||||||
|
* @return Pointer to mapped data memory (at src_addr), or NULL
|
||||||
|
* if an allocation error occured.
|
||||||
|
*/
|
||||||
|
const void *bootloader_mmap(uint32_t src_addr, uint32_t size);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unmap a previously mapped region of flash
|
||||||
|
*
|
||||||
|
* Call bootloader_munmap once for each successful call to bootloader_mmap.
|
||||||
|
*/
|
||||||
|
void bootloader_munmap(const void *mapping);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read data from Flash.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @note All of src, dest and size have to be 4-byte aligned.
|
||||||
|
*
|
||||||
|
* @param src source address of the data in Flash.
|
||||||
|
* @param dest pointer to the destination buffer
|
||||||
|
* @param size length of data
|
||||||
|
* @param allow_decrypt If true and flash encryption is enabled, data on flash
|
||||||
|
* will be decrypted transparently as part of the read.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success, ESP_ERR_FLASH_OP_FAIL on SPI failure,
|
||||||
|
* ESP_ERR_FLASH_OP_TIMEOUT on SPI timeout.
|
||||||
|
*/
|
||||||
|
esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size, bool allow_decrypt);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write data to Flash.
|
||||||
|
*
|
||||||
|
* @note All of dest_addr, src and size have to be 4-byte aligned. If write_encrypted is set, dest_addr and size must be 32-byte aligned.
|
||||||
|
*
|
||||||
|
* Note: In bootloader, when write_encrypted == true, the src buffer is encrypted in place.
|
||||||
|
*
|
||||||
|
* @param dest_addr Destination address to write in Flash.
|
||||||
|
* @param src Pointer to the data to write to flash
|
||||||
|
* @param size Length of data in bytes.
|
||||||
|
* @param write_encrypted If true, data will be written encrypted on flash.
|
||||||
|
*
|
||||||
|
* @return ESP_OK on success, ESP_ERR_FLASH_OP_FAIL on SPI failure,
|
||||||
|
* ESP_ERR_FLASH_OP_TIMEOUT on SPI timeout.
|
||||||
|
*/
|
||||||
|
esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool write_encrypted);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Erase the Flash sector.
|
||||||
|
*
|
||||||
|
* @param sector Sector number, the count starts at sector 0, 4KB per sector.
|
||||||
|
*
|
||||||
|
* @return esp_err_t
|
||||||
|
*/
|
||||||
|
esp_err_t bootloader_flash_erase_sector(size_t sector);
|
||||||
|
|
||||||
|
#endif
|
28
components/bootloader_support/include_priv/bootloader_init.h
Normal file
28
components/bootloader_support/include_priv/bootloader_init.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
/* @brief Prepares hardware for work.
|
||||||
|
*
|
||||||
|
* Setting up:
|
||||||
|
* - Disable Cache access for both CPUs;
|
||||||
|
* - Initialise cache mmu;
|
||||||
|
* - Setting up pins and mode for SD, SPI, UART, Clocking.
|
||||||
|
|
||||||
|
* @return ESP_OK - If the setting is successful.
|
||||||
|
* ESP_FAIL - If the setting is not successful.
|
||||||
|
*/
|
||||||
|
esp_err_t bootloader_init();
|
@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enable early entropy source for RNG
|
||||||
|
*
|
||||||
|
* Uses the SAR ADC to feed entropy into the HWRNG. The ADC is put
|
||||||
|
* into a test mode that reads the 1.1V internal reference source and
|
||||||
|
* feeds the LSB of data into the HWRNG.
|
||||||
|
*
|
||||||
|
* Can also be used from app code early during operation, if entropy
|
||||||
|
* is required before WiFi stack is initialised. Call this function
|
||||||
|
* from app code only if WiFi/BT are not yet enabled and I2S and SAR
|
||||||
|
* ADC are not in use.
|
||||||
|
*
|
||||||
|
* Call bootloader_random_disable() when done.
|
||||||
|
*/
|
||||||
|
void bootloader_random_enable(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Disable early entropy source for RNG
|
||||||
|
*
|
||||||
|
* Disables SAR ADC source and resets the I2S hardware.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void bootloader_random_disable(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fill buffer with 'length' random bytes
|
||||||
|
*
|
||||||
|
* @param buffer Pointer to buffer
|
||||||
|
* @param length This many bytes of random data will be copied to buffer
|
||||||
|
*/
|
||||||
|
void bootloader_fill_random(void *buffer, size_t length);
|
32
components/bootloader_support/include_priv/bootloader_sha.h
Normal file
32
components/bootloader_support/include_priv/bootloader_sha.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/* Provide a SHA256 API for bootloader_support code,
|
||||||
|
that can be used from bootloader or app code.
|
||||||
|
|
||||||
|
This header is available to source code in the bootloader & bootloader_support components only.
|
||||||
|
Use mbedTLS APIs or include hwcrypto/sha.h to calculate SHA256 in IDF apps.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef void *bootloader_sha256_handle_t;
|
||||||
|
|
||||||
|
bootloader_sha256_handle_t bootloader_sha256_start();
|
||||||
|
|
||||||
|
void bootloader_sha256_data(bootloader_sha256_handle_t handle, const void *data, size_t data_len);
|
||||||
|
|
||||||
|
void bootloader_sha256_finish(bootloader_sha256_handle_t handle, uint8_t *digest);
|
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esp_image_format.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load partition table.
|
||||||
|
*
|
||||||
|
* Parse partition table, get useful data such as location of
|
||||||
|
* OTA data partition, factory app partition, and test app partition.
|
||||||
|
*
|
||||||
|
* @param[out] bs Bootloader state structure used to save read data.
|
||||||
|
* @return Return true if the partition table was succesfully loaded and MD5 checksum is valid.
|
||||||
|
*/
|
||||||
|
bool bootloader_utility_load_partition_table(bootloader_state_t* bs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the index of the selected boot partition.
|
||||||
|
*
|
||||||
|
* This is the preferred boot partition, as determined by the partition table &
|
||||||
|
* any OTA sequence number found in OTA data.
|
||||||
|
* This partition will only be booted if it contains a valid app image, otherwise load_boot_image() will search
|
||||||
|
* for a valid partition using this selection as the starting point.
|
||||||
|
*
|
||||||
|
* @param[in] bs Bootloader state structure.
|
||||||
|
* @return Returns the index on success, INVALID_INDEX otherwise.
|
||||||
|
*/
|
||||||
|
int bootloader_utility_get_selected_boot_partition(const bootloader_state_t *bs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load the app image for booting.
|
||||||
|
*
|
||||||
|
* Start from partition 'start_index', if not bootable then work backwards to FACTORY_INDEX
|
||||||
|
* (ie try any OTA slots in descending order and then the factory partition).
|
||||||
|
* If still nothing, start from 'start_index + 1' and work up to highest numbered OTA partition.
|
||||||
|
* If still nothing, try TEST_APP_INDEX.
|
||||||
|
*
|
||||||
|
* @param[in] bs Bootloader state structure.
|
||||||
|
* @param[in] start_index The index from which the search for images begins.
|
||||||
|
* @param[out] result The image found.
|
||||||
|
* @return Returns true on success, false if there's no bootable app in the partition table.
|
||||||
|
*/
|
||||||
|
bool bootloader_utility_load_boot_image(const bootloader_state_t *bs, int start_index, esp_image_metadata_t *result);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Loading the selected image.
|
||||||
|
*
|
||||||
|
* Copy loaded segments to RAM, set up caches for mapped segments, and start application.
|
||||||
|
*
|
||||||
|
* @param[in] data Structure to hold on-flash image metadata.
|
||||||
|
*/
|
||||||
|
void bootloader_utility_load_image(const esp_image_metadata_t* image_data);
|
37
components/bootloader_support/include_priv/flash_qio_mode.h
Normal file
37
components/bootloader_support/include_priv/flash_qio_mode.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @brief Enable Quad I/O mode in bootloader (if configured)
|
||||||
|
*
|
||||||
|
* Queries attached SPI flash ID and sends correct SPI flash
|
||||||
|
* commands to enable QIO or QOUT mode, then enables this mode.
|
||||||
|
*/
|
||||||
|
void bootloader_enable_qio_mode(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Read flash ID by sending 0x9F command
|
||||||
|
* @return flash raw ID
|
||||||
|
* mfg_id = (ID >> 16) & 0xFF;
|
||||||
|
flash_id = ID & 0xffff;
|
||||||
|
*/
|
||||||
|
uint32_t bootloader_read_flash_id();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
61
components/bootloader_support/src/bootloader_clock.c
Normal file
61
components/bootloader_support/src/bootloader_clock.c
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include "rom/uart.h"
|
||||||
|
#include "rom/rtc.h"
|
||||||
|
#include "soc/soc.h"
|
||||||
|
#include "soc/rtc.h"
|
||||||
|
#include "soc/dport_reg.h"
|
||||||
|
#include "soc/efuse_reg.h"
|
||||||
|
#include "soc/rtc_cntl_reg.h"
|
||||||
|
|
||||||
|
void bootloader_clock_configure()
|
||||||
|
{
|
||||||
|
// ROM bootloader may have put a lot of text into UART0 FIFO.
|
||||||
|
// Wait for it to be printed.
|
||||||
|
// This is not needed on power on reset, when ROM bootloader is running at
|
||||||
|
// 40 MHz. But in case of TG WDT reset, CPU may still be running at >80 MHZ,
|
||||||
|
// and will be done with the bootloader much earlier than UART FIFO is empty.
|
||||||
|
uart_tx_wait_idle(0);
|
||||||
|
|
||||||
|
/* Set CPU to 80MHz. Keep other clocks unmodified. */
|
||||||
|
rtc_cpu_freq_t cpu_freq = RTC_CPU_FREQ_80M;
|
||||||
|
|
||||||
|
/* On ESP32 rev 0, switching to 80MHz if clock was previously set to
|
||||||
|
* 240 MHz may cause the chip to lock up (see section 3.5 of the errata
|
||||||
|
* document). For rev. 0, switch to 240 instead if it was chosen in
|
||||||
|
* menuconfig.
|
||||||
|
*/
|
||||||
|
uint32_t chip_ver_reg = REG_READ(EFUSE_BLK0_RDATA3_REG);
|
||||||
|
if ((chip_ver_reg & EFUSE_RD_CHIP_VER_REV1_M) == 0 &&
|
||||||
|
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ == 240) {
|
||||||
|
cpu_freq = RTC_CPU_FREQ_240M;
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_clk_config_t clk_cfg = RTC_CLK_CONFIG_DEFAULT();
|
||||||
|
clk_cfg.xtal_freq = CONFIG_ESP32_XTAL_FREQ;
|
||||||
|
clk_cfg.cpu_freq = cpu_freq;
|
||||||
|
clk_cfg.slow_freq = rtc_clk_slow_freq_get();
|
||||||
|
clk_cfg.fast_freq = rtc_clk_fast_freq_get();
|
||||||
|
rtc_clk_init(clk_cfg);
|
||||||
|
/* As a slight optimization, if 32k XTAL was enabled in sdkconfig, we enable
|
||||||
|
* it here. Usually it needs some time to start up, so we amortize at least
|
||||||
|
* part of the start up time by enabling 32k XTAL early.
|
||||||
|
* App startup code will wait until the oscillator has started up.
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL
|
||||||
|
if (!rtc_clk_32k_enabled()) {
|
||||||
|
rtc_clk_32k_bootstrap(CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
155
components/bootloader_support/src/bootloader_common.c
Normal file
155
components/bootloader_support/src/bootloader_common.c
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "string.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "rom/spi_flash.h"
|
||||||
|
#include "rom/crc.h"
|
||||||
|
#include "rom/ets_sys.h"
|
||||||
|
#include "rom/gpio.h"
|
||||||
|
#include "esp_flash_data_types.h"
|
||||||
|
#include "esp_secure_boot.h"
|
||||||
|
#include "esp_flash_partitions.h"
|
||||||
|
#include "bootloader_flash.h"
|
||||||
|
#include "bootloader_common.h"
|
||||||
|
|
||||||
|
static const char* TAG = "boot_comm";
|
||||||
|
|
||||||
|
uint32_t bootloader_common_ota_select_crc(const esp_ota_select_entry_t *s)
|
||||||
|
{
|
||||||
|
return crc32_le(UINT32_MAX, (uint8_t*)&s->ota_seq, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bootloader_common_ota_select_valid(const esp_ota_select_entry_t *s)
|
||||||
|
{
|
||||||
|
return s->ota_seq != UINT32_MAX && s->crc == bootloader_common_ota_select_crc(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_comm_gpio_hold_t bootloader_common_check_long_hold_gpio(uint32_t num_pin, uint32_t delay_sec)
|
||||||
|
{
|
||||||
|
gpio_pad_select_gpio(num_pin);
|
||||||
|
gpio_pad_pullup(num_pin);
|
||||||
|
uint32_t tm_start = esp_log_early_timestamp();
|
||||||
|
if (GPIO_INPUT_GET(num_pin) == 1) {
|
||||||
|
return GPIO_NOT_HOLD;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
if (GPIO_INPUT_GET(num_pin) != 0) {
|
||||||
|
return GPIO_SHORT_HOLD;
|
||||||
|
}
|
||||||
|
} while (delay_sec > ((esp_log_early_timestamp() - tm_start) / 1000L));
|
||||||
|
return GPIO_LONG_HOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for a label in the list. list = "nvs1, nvs2, otadata, nvs"; label = "nvs".
|
||||||
|
bool bootloader_common_label_search(const char *list, char *label)
|
||||||
|
{
|
||||||
|
if (list == NULL || label == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const char *sub_list_start_like_label = strstr(list, label);
|
||||||
|
while (sub_list_start_like_label != NULL) {
|
||||||
|
|
||||||
|
// ["," or " "] + label + ["," or " " or "\0"]
|
||||||
|
// first character before the label found there must be a delimiter ["," or " "].
|
||||||
|
int idx_first = sub_list_start_like_label - list;
|
||||||
|
if (idx_first == 0 || (idx_first != 0 && (list[idx_first - 1] == ',' || list[idx_first - 1] == ' '))) {
|
||||||
|
// next character after the label found there must be a delimiter ["," or " " or "\0"].
|
||||||
|
int len_label = strlen(label);
|
||||||
|
if (sub_list_start_like_label[len_label] == 0 ||
|
||||||
|
sub_list_start_like_label[len_label] == ',' ||
|
||||||
|
sub_list_start_like_label[len_label] == ' ') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// [start_delim] + label + [end_delim] was not found.
|
||||||
|
// Position is moving to next delimiter if it is not the end of list.
|
||||||
|
int pos_delim = strcspn(sub_list_start_like_label, ", ");
|
||||||
|
if (pos_delim == strlen(sub_list_start_like_label)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sub_list_start_like_label = strstr(&sub_list_start_like_label[pos_delim], label);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bootloader_common_erase_part_type_data(const char *list_erase, bool ota_data_erase)
|
||||||
|
{
|
||||||
|
const esp_partition_info_t *partitions;
|
||||||
|
const char *marker;
|
||||||
|
esp_err_t err;
|
||||||
|
int num_partitions;
|
||||||
|
bool ret = true;
|
||||||
|
|
||||||
|
#ifdef CONFIG_SECURE_BOOT_ENABLED
|
||||||
|
if (esp_secure_boot_enabled()) {
|
||||||
|
ESP_LOGI(TAG, "Verifying partition table signature...");
|
||||||
|
err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_MAX_LEN);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to verify partition table signature.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Partition table signature verified");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
partitions = bootloader_mmap(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_MAX_LEN);
|
||||||
|
if (!partitions) {
|
||||||
|
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_MAX_LEN);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", ESP_PARTITION_TABLE_ADDR, (intptr_t)partitions);
|
||||||
|
|
||||||
|
err = esp_partition_table_basic_verify(partitions, true, &num_partitions);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to verify partition table");
|
||||||
|
ret = false;
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "## Label Usage Offset Length Cleaned");
|
||||||
|
for (int i = 0; i < num_partitions; i++) {
|
||||||
|
const esp_partition_info_t *partition = &partitions[i];
|
||||||
|
char label[sizeof(partition->label) + 1] = {0};
|
||||||
|
if (partition->type == PART_TYPE_DATA) {
|
||||||
|
bool fl_ota_data_erase = false;
|
||||||
|
if (ota_data_erase == true && partition->subtype == PART_SUBTYPE_DATA_OTA) {
|
||||||
|
fl_ota_data_erase = true;
|
||||||
|
}
|
||||||
|
// partition->label is not null-terminated string.
|
||||||
|
strncpy(label, (char *)&partition->label, sizeof(partition->label));
|
||||||
|
if (fl_ota_data_erase == true || (bootloader_common_label_search(list_erase, label) == true)) {
|
||||||
|
err = esp_rom_spiflash_erase_area(partition->pos.offset, partition->pos.size);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ret = false;
|
||||||
|
marker = "err";
|
||||||
|
} else {
|
||||||
|
marker = "yes";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
marker = "no";
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "%2d %-16s data %08x %08x [%s]", i, partition->label,
|
||||||
|
partition->pos.offset, partition->pos.size, marker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bootloader_munmap(partitions);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
250
components/bootloader_support/src/bootloader_flash.c
Normal file
250
components/bootloader_support/src/bootloader_flash.c
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <bootloader_flash.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_spi_flash.h> /* including in bootloader for error values */
|
||||||
|
#include <esp_flash_encrypt.h>
|
||||||
|
|
||||||
|
#ifndef BOOTLOADER_BUILD
|
||||||
|
/* Normal app version maps to esp_spi_flash.h operations...
|
||||||
|
*/
|
||||||
|
static const char *TAG = "bootloader_mmap";
|
||||||
|
|
||||||
|
static spi_flash_mmap_handle_t map;
|
||||||
|
|
||||||
|
const void *bootloader_mmap(uint32_t src_addr, uint32_t size)
|
||||||
|
{
|
||||||
|
if (map) {
|
||||||
|
ESP_LOGE(TAG, "tried to bootloader_mmap twice");
|
||||||
|
return NULL; /* existing mapping in use... */
|
||||||
|
}
|
||||||
|
const void *result = NULL;
|
||||||
|
uint32_t src_page = src_addr & ~(SPI_FLASH_MMU_PAGE_SIZE-1);
|
||||||
|
size += (src_addr - src_page);
|
||||||
|
esp_err_t err = spi_flash_mmap(src_page, size, SPI_FLASH_MMAP_DATA, &result, &map);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "spi_flash_mmap failed: 0x%x", err);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return (void *)((intptr_t)result + (src_addr - src_page));
|
||||||
|
}
|
||||||
|
|
||||||
|
void bootloader_munmap(const void *mapping)
|
||||||
|
{
|
||||||
|
if(mapping && map) {
|
||||||
|
spi_flash_munmap(map);
|
||||||
|
}
|
||||||
|
map = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bootloader_flash_read(size_t src, void *dest, size_t size, bool allow_decrypt)
|
||||||
|
{
|
||||||
|
if (allow_decrypt && esp_flash_encryption_enabled()) {
|
||||||
|
return spi_flash_read_encrypted(src, dest, size);
|
||||||
|
} else {
|
||||||
|
return spi_flash_read(src, dest, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool write_encrypted)
|
||||||
|
{
|
||||||
|
if (write_encrypted) {
|
||||||
|
return spi_flash_write_encrypted(dest_addr, src, size);
|
||||||
|
} else {
|
||||||
|
return spi_flash_write(dest_addr, src, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bootloader_flash_erase_sector(size_t sector)
|
||||||
|
{
|
||||||
|
return spi_flash_erase_sector(sector);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
/* Bootloader version, uses ROM functions only */
|
||||||
|
#include <soc/dport_reg.h>
|
||||||
|
#include <rom/spi_flash.h>
|
||||||
|
#include <rom/cache.h>
|
||||||
|
|
||||||
|
static const char *TAG = "bootloader_flash";
|
||||||
|
|
||||||
|
/* Use first 50 blocks in MMU for bootloader_mmap,
|
||||||
|
50th block for bootloader_flash_read
|
||||||
|
*/
|
||||||
|
#define MMU_BLOCK0_VADDR 0x3f400000
|
||||||
|
#define MMU_BLOCK50_VADDR 0x3f720000
|
||||||
|
#define MMU_FLASH_MASK 0xffff0000
|
||||||
|
#define MMU_BLOCK_SIZE 0x00010000
|
||||||
|
|
||||||
|
static bool mapped;
|
||||||
|
|
||||||
|
// Current bootloader mapping (ab)used for bootloader_read()
|
||||||
|
static uint32_t current_read_mapping = UINT32_MAX;
|
||||||
|
|
||||||
|
const void *bootloader_mmap(uint32_t src_addr, uint32_t size)
|
||||||
|
{
|
||||||
|
if (mapped) {
|
||||||
|
ESP_LOGE(TAG, "tried to bootloader_mmap twice");
|
||||||
|
return NULL; /* can't map twice */
|
||||||
|
}
|
||||||
|
if (size > 0x320000) {
|
||||||
|
/* Allow mapping up to 50 of the 51 available MMU blocks (last one used for reads) */
|
||||||
|
ESP_LOGE(TAG, "bootloader_mmap excess size %x", size);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t src_addr_aligned = src_addr & MMU_FLASH_MASK;
|
||||||
|
uint32_t count = (size + (src_addr - src_addr_aligned) + 0xffff) / MMU_BLOCK_SIZE;
|
||||||
|
Cache_Read_Disable(0);
|
||||||
|
Cache_Flush(0);
|
||||||
|
ESP_LOGD(TAG, "mmu set paddr=%08x count=%d", src_addr_aligned, count );
|
||||||
|
int e = cache_flash_mmu_set(0, 0, MMU_BLOCK0_VADDR, src_addr_aligned, 64, count);
|
||||||
|
if (e != 0) {
|
||||||
|
ESP_LOGE(TAG, "cache_flash_mmu_set failed: %d\n", e);
|
||||||
|
Cache_Read_Enable(0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Cache_Read_Enable(0);
|
||||||
|
|
||||||
|
mapped = true;
|
||||||
|
|
||||||
|
return (void *)(MMU_BLOCK0_VADDR + (src_addr - src_addr_aligned));
|
||||||
|
}
|
||||||
|
|
||||||
|
void bootloader_munmap(const void *mapping)
|
||||||
|
{
|
||||||
|
if (mapped) {
|
||||||
|
/* Full MMU reset */
|
||||||
|
Cache_Read_Disable(0);
|
||||||
|
Cache_Flush(0);
|
||||||
|
mmu_init(0);
|
||||||
|
mapped = false;
|
||||||
|
current_read_mapping = UINT32_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t spi_to_esp_err(esp_rom_spiflash_result_t r)
|
||||||
|
{
|
||||||
|
switch(r) {
|
||||||
|
case ESP_ROM_SPIFLASH_RESULT_OK:
|
||||||
|
return ESP_OK;
|
||||||
|
case ESP_ROM_SPIFLASH_RESULT_ERR:
|
||||||
|
return ESP_ERR_FLASH_OP_FAIL;
|
||||||
|
case ESP_ROM_SPIFLASH_RESULT_TIMEOUT:
|
||||||
|
return ESP_ERR_FLASH_OP_TIMEOUT;
|
||||||
|
default:
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t bootloader_flash_read_no_decrypt(size_t src_addr, void *dest, size_t size)
|
||||||
|
{
|
||||||
|
Cache_Read_Disable(0);
|
||||||
|
Cache_Flush(0);
|
||||||
|
esp_rom_spiflash_result_t r = esp_rom_spiflash_read(src_addr, dest, size);
|
||||||
|
Cache_Read_Enable(0);
|
||||||
|
|
||||||
|
return spi_to_esp_err(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t bootloader_flash_read_allow_decrypt(size_t src_addr, void *dest, size_t size)
|
||||||
|
{
|
||||||
|
uint32_t *dest_words = (uint32_t *)dest;
|
||||||
|
|
||||||
|
/* Use the 51st MMU mapping to read from flash in 64KB blocks.
|
||||||
|
(MMU will transparently decrypt if encryption is enabled.)
|
||||||
|
*/
|
||||||
|
for (int word = 0; word < size / 4; word++) {
|
||||||
|
uint32_t word_src = src_addr + word * 4; /* Read this offset from flash */
|
||||||
|
uint32_t map_at = word_src & MMU_FLASH_MASK; /* Map this 64KB block from flash */
|
||||||
|
uint32_t *map_ptr;
|
||||||
|
if (map_at != current_read_mapping) {
|
||||||
|
/* Move the 64KB mmu mapping window to fit map_at */
|
||||||
|
Cache_Read_Disable(0);
|
||||||
|
Cache_Flush(0);
|
||||||
|
ESP_LOGD(TAG, "mmu set block paddr=0x%08x (was 0x%08x)", map_at, current_read_mapping);
|
||||||
|
int e = cache_flash_mmu_set(0, 0, MMU_BLOCK50_VADDR, map_at, 64, 1);
|
||||||
|
if (e != 0) {
|
||||||
|
ESP_LOGE(TAG, "cache_flash_mmu_set failed: %d\n", e);
|
||||||
|
Cache_Read_Enable(0);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
current_read_mapping = map_at;
|
||||||
|
Cache_Read_Enable(0);
|
||||||
|
}
|
||||||
|
map_ptr = (uint32_t *)(MMU_BLOCK50_VADDR + (word_src - map_at));
|
||||||
|
dest_words[word] = *map_ptr;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size, bool allow_decrypt)
|
||||||
|
{
|
||||||
|
if (src_addr & 3) {
|
||||||
|
ESP_LOGE(TAG, "bootloader_flash_read src_addr 0x%x not 4-byte aligned", src_addr);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if (size & 3) {
|
||||||
|
ESP_LOGE(TAG, "bootloader_flash_read size 0x%x not 4-byte aligned", size);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if ((intptr_t)dest & 3) {
|
||||||
|
ESP_LOGE(TAG, "bootloader_flash_read dest 0x%x not 4-byte aligned", (intptr_t)dest);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allow_decrypt) {
|
||||||
|
return bootloader_flash_read_allow_decrypt(src_addr, dest, size);
|
||||||
|
} else {
|
||||||
|
return bootloader_flash_read_no_decrypt(src_addr, dest, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool write_encrypted)
|
||||||
|
{
|
||||||
|
esp_err_t err;
|
||||||
|
size_t alignment = write_encrypted ? 32 : 4;
|
||||||
|
if ((dest_addr % alignment) != 0) {
|
||||||
|
ESP_LOGE(TAG, "bootloader_flash_write dest_addr 0x%x not %d-byte aligned", dest_addr, alignment);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if ((size % alignment) != 0) {
|
||||||
|
ESP_LOGE(TAG, "bootloader_flash_write size 0x%x not %d-byte aligned", size, alignment);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
if (((intptr_t)src % 4) != 0) {
|
||||||
|
ESP_LOGE(TAG, "bootloader_flash_write src 0x%x not 4 byte aligned", (intptr_t)src);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = spi_to_esp_err(esp_rom_spiflash_unlock());
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write_encrypted) {
|
||||||
|
return spi_to_esp_err(esp_rom_spiflash_write_encrypted(dest_addr, src, size));
|
||||||
|
} else {
|
||||||
|
return spi_to_esp_err(esp_rom_spiflash_write(dest_addr, src, size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t bootloader_flash_erase_sector(size_t sector)
|
||||||
|
{
|
||||||
|
return spi_to_esp_err(esp_rom_spiflash_erase_sector(sector));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
530
components/bootloader_support/src/bootloader_init.c
Normal file
530
components/bootloader_support/src/bootloader_init.c
Normal file
@ -0,0 +1,530 @@
|
|||||||
|
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
//#include <limits.h>
|
||||||
|
//#include <sys/param.h>
|
||||||
|
|
||||||
|
#include "esp_attr.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#include "rom/cache.h"
|
||||||
|
#include "rom/efuse.h"
|
||||||
|
#include "rom/ets_sys.h"
|
||||||
|
#include "rom/spi_flash.h"
|
||||||
|
#include "rom/crc.h"
|
||||||
|
#include "rom/rtc.h"
|
||||||
|
#include "rom/uart.h"
|
||||||
|
#include "rom/gpio.h"
|
||||||
|
#include "rom/secure_boot.h"
|
||||||
|
|
||||||
|
#include "soc/soc.h"
|
||||||
|
#include "soc/cpu.h"
|
||||||
|
#include "soc/rtc.h"
|
||||||
|
#include "soc/dport_reg.h"
|
||||||
|
#include "soc/io_mux_reg.h"
|
||||||
|
#include "soc/efuse_reg.h"
|
||||||
|
#include "soc/rtc_cntl_reg.h"
|
||||||
|
#include "soc/timer_group_reg.h"
|
||||||
|
#include "soc/gpio_reg.h"
|
||||||
|
#include "soc/gpio_sig_map.h"
|
||||||
|
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "esp_image_format.h"
|
||||||
|
#include "esp_secure_boot.h"
|
||||||
|
#include "esp_flash_encrypt.h"
|
||||||
|
#include "esp_flash_partitions.h"
|
||||||
|
#include "bootloader_flash.h"
|
||||||
|
#include "bootloader_random.h"
|
||||||
|
#include "bootloader_config.h"
|
||||||
|
#include "bootloader_clock.h"
|
||||||
|
|
||||||
|
#include "flash_qio_mode.h"
|
||||||
|
|
||||||
|
extern int _bss_start;
|
||||||
|
extern int _bss_end;
|
||||||
|
extern int _data_start;
|
||||||
|
extern int _data_end;
|
||||||
|
|
||||||
|
static const char* TAG = "boot";
|
||||||
|
|
||||||
|
static esp_err_t bootloader_main();
|
||||||
|
static void print_flash_info(const esp_image_header_t* pfhdr);
|
||||||
|
static void update_flash_config(const esp_image_header_t* pfhdr);
|
||||||
|
static void vddsdio_configure();
|
||||||
|
static void flash_gpio_configure(const esp_image_header_t* pfhdr);
|
||||||
|
static void uart_console_configure(void);
|
||||||
|
static void wdt_reset_check(void);
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t bootloader_init()
|
||||||
|
{
|
||||||
|
cpu_configure_region_protection();
|
||||||
|
|
||||||
|
/* Sanity check that static RAM is after the stack */
|
||||||
|
#ifndef NDEBUG
|
||||||
|
{
|
||||||
|
int *sp = get_sp();
|
||||||
|
assert(&_bss_start <= &_bss_end);
|
||||||
|
assert(&_data_start <= &_data_end);
|
||||||
|
assert(sp < &_bss_start);
|
||||||
|
assert(sp < &_data_start);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//Clear bss
|
||||||
|
memset(&_bss_start, 0, (&_bss_end - &_bss_start) * sizeof(_bss_start));
|
||||||
|
|
||||||
|
/* completely reset MMU for both CPUs
|
||||||
|
(in case serial bootloader was running) */
|
||||||
|
Cache_Read_Disable(0);
|
||||||
|
Cache_Read_Disable(1);
|
||||||
|
Cache_Flush(0);
|
||||||
|
Cache_Flush(1);
|
||||||
|
mmu_init(0);
|
||||||
|
DPORT_REG_SET_BIT(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MMU_IA_CLR);
|
||||||
|
mmu_init(1);
|
||||||
|
DPORT_REG_CLR_BIT(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MMU_IA_CLR);
|
||||||
|
/* (above steps probably unnecessary for most serial bootloader
|
||||||
|
usage, all that's absolutely needed is that we unmask DROM0
|
||||||
|
cache on the following two lines - normal ROM boot exits with
|
||||||
|
DROM0 cache unmasked, but serial bootloader exits with it
|
||||||
|
masked. However can't hurt to be thorough and reset
|
||||||
|
everything.)
|
||||||
|
|
||||||
|
The lines which manipulate DPORT_APP_CACHE_MMU_IA_CLR bit are
|
||||||
|
necessary to work around a hardware bug.
|
||||||
|
*/
|
||||||
|
DPORT_REG_CLR_BIT(DPORT_PRO_CACHE_CTRL1_REG, DPORT_PRO_CACHE_MASK_DROM0);
|
||||||
|
DPORT_REG_CLR_BIT(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MASK_DROM0);
|
||||||
|
|
||||||
|
if(bootloader_main() != ESP_OK){
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t bootloader_main()
|
||||||
|
{
|
||||||
|
vddsdio_configure();
|
||||||
|
/* Read and keep flash ID, for further use. */
|
||||||
|
g_rom_flashchip.device_id = bootloader_read_flash_id();
|
||||||
|
esp_image_header_t fhdr;
|
||||||
|
if (bootloader_flash_read(ESP_BOOTLOADER_OFFSET, &fhdr, sizeof(esp_image_header_t), true) != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "failed to load bootloader header!");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
flash_gpio_configure(&fhdr);
|
||||||
|
#if (CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ == 240)
|
||||||
|
//Check if ESP32 is rated for a CPU frequency of 160MHz only
|
||||||
|
if (REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_CPU_FREQ_RATED) &&
|
||||||
|
REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_CPU_FREQ_LOW)) {
|
||||||
|
ESP_LOGE(TAG, "Chip CPU frequency rated for 160MHz. Modify CPU frequency in menuconfig");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
bootloader_clock_configure();
|
||||||
|
uart_console_configure();
|
||||||
|
wdt_reset_check();
|
||||||
|
ESP_LOGI(TAG, "ESP-IDF %s 2nd stage bootloader", IDF_VER);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "compile time " __TIME__ );
|
||||||
|
ets_set_appcpu_boot_addr(0);
|
||||||
|
|
||||||
|
/* disable watch dog here */
|
||||||
|
REG_CLR_BIT( RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN );
|
||||||
|
REG_CLR_BIT( TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN );
|
||||||
|
|
||||||
|
#ifndef CONFIG_SPI_FLASH_ROM_DRIVER_PATCH
|
||||||
|
const uint32_t spiconfig = ets_efuse_get_spiconfig();
|
||||||
|
if(spiconfig != EFUSE_SPICONFIG_SPI_DEFAULTS && spiconfig != EFUSE_SPICONFIG_HSPI_DEFAULTS) {
|
||||||
|
ESP_LOGE(TAG, "SPI flash pins are overridden. \"Enable SPI flash ROM driver patched functions\" must be enabled in menuconfig");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
esp_rom_spiflash_unlock();
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Enabling RNG early entropy source...");
|
||||||
|
bootloader_random_enable();
|
||||||
|
|
||||||
|
#if CONFIG_FLASHMODE_QIO || CONFIG_FLASHMODE_QOUT
|
||||||
|
bootloader_enable_qio_mode();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
print_flash_info(&fhdr);
|
||||||
|
|
||||||
|
update_flash_config(&fhdr);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_flash_config(const esp_image_header_t* pfhdr)
|
||||||
|
{
|
||||||
|
uint32_t size;
|
||||||
|
switch(pfhdr->spi_size) {
|
||||||
|
case ESP_IMAGE_FLASH_SIZE_1MB:
|
||||||
|
size = 1;
|
||||||
|
break;
|
||||||
|
case ESP_IMAGE_FLASH_SIZE_2MB:
|
||||||
|
size = 2;
|
||||||
|
break;
|
||||||
|
case ESP_IMAGE_FLASH_SIZE_4MB:
|
||||||
|
size = 4;
|
||||||
|
break;
|
||||||
|
case ESP_IMAGE_FLASH_SIZE_8MB:
|
||||||
|
size = 8;
|
||||||
|
break;
|
||||||
|
case ESP_IMAGE_FLASH_SIZE_16MB:
|
||||||
|
size = 16;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
size = 2;
|
||||||
|
}
|
||||||
|
Cache_Read_Disable( 0 );
|
||||||
|
// Set flash chip size
|
||||||
|
esp_rom_spiflash_config_param(g_rom_flashchip.device_id, size * 0x100000, 0x10000, 0x1000, 0x100, 0xffff);
|
||||||
|
// TODO: set mode
|
||||||
|
// TODO: set frequency
|
||||||
|
Cache_Flush(0);
|
||||||
|
Cache_Read_Enable( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_flash_info(const esp_image_header_t* phdr)
|
||||||
|
{
|
||||||
|
#if (BOOT_LOG_LEVEL >= BOOT_LOG_LEVEL_NOTICE)
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "magic %02x", phdr->magic );
|
||||||
|
ESP_LOGD(TAG, "segments %02x", phdr->segment_count );
|
||||||
|
ESP_LOGD(TAG, "spi_mode %02x", phdr->spi_mode );
|
||||||
|
ESP_LOGD(TAG, "spi_speed %02x", phdr->spi_speed );
|
||||||
|
ESP_LOGD(TAG, "spi_size %02x", phdr->spi_size );
|
||||||
|
|
||||||
|
const char* str;
|
||||||
|
switch ( phdr->spi_speed ) {
|
||||||
|
case ESP_IMAGE_SPI_SPEED_40M:
|
||||||
|
str = "40MHz";
|
||||||
|
break;
|
||||||
|
case ESP_IMAGE_SPI_SPEED_26M:
|
||||||
|
str = "26.7MHz";
|
||||||
|
break;
|
||||||
|
case ESP_IMAGE_SPI_SPEED_20M:
|
||||||
|
str = "20MHz";
|
||||||
|
break;
|
||||||
|
case ESP_IMAGE_SPI_SPEED_80M:
|
||||||
|
str = "80MHz";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str = "20MHz";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "SPI Speed : %s", str );
|
||||||
|
|
||||||
|
extern uint32_t g_ticks_per_us_pro;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "g_ticks_per_us_pro: %d", g_ticks_per_us_pro );
|
||||||
|
|
||||||
|
|
||||||
|
/* SPI mode could have been set to QIO during boot already,
|
||||||
|
so test the SPI registers not the flash header */
|
||||||
|
uint32_t spi_ctrl = REG_READ(SPI_CTRL_REG(0));
|
||||||
|
if (spi_ctrl & SPI_FREAD_QIO) {
|
||||||
|
str = "QIO";
|
||||||
|
} else if (spi_ctrl & SPI_FREAD_QUAD) {
|
||||||
|
str = "QOUT";
|
||||||
|
} else if (spi_ctrl & SPI_FREAD_DIO) {
|
||||||
|
str = "DIO";
|
||||||
|
} else if (spi_ctrl & SPI_FREAD_DUAL) {
|
||||||
|
str = "DOUT";
|
||||||
|
} else if (spi_ctrl & SPI_FASTRD_MODE) {
|
||||||
|
str = "FAST READ";
|
||||||
|
} else {
|
||||||
|
str = "SLOW READ";
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "SPI Mode : %s", str );
|
||||||
|
|
||||||
|
switch ( phdr->spi_size ) {
|
||||||
|
case ESP_IMAGE_FLASH_SIZE_1MB:
|
||||||
|
str = "1MB";
|
||||||
|
break;
|
||||||
|
case ESP_IMAGE_FLASH_SIZE_2MB:
|
||||||
|
str = "2MB";
|
||||||
|
break;
|
||||||
|
case ESP_IMAGE_FLASH_SIZE_4MB:
|
||||||
|
str = "4MB";
|
||||||
|
break;
|
||||||
|
case ESP_IMAGE_FLASH_SIZE_8MB:
|
||||||
|
str = "8MB";
|
||||||
|
break;
|
||||||
|
case ESP_IMAGE_FLASH_SIZE_16MB:
|
||||||
|
str = "16MB";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str = "2MB";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "SPI Flash Size : %s", str );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vddsdio_configure()
|
||||||
|
{
|
||||||
|
#if CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V
|
||||||
|
rtc_vddsdio_config_t cfg = rtc_vddsdio_get_config();
|
||||||
|
if (cfg.enable == 1 && cfg.tieh == RTC_VDDSDIO_TIEH_1_8V) { // VDDSDIO regulator is enabled @ 1.8V
|
||||||
|
cfg.drefh = 3;
|
||||||
|
cfg.drefm = 3;
|
||||||
|
cfg.drefl = 3;
|
||||||
|
cfg.force = 1;
|
||||||
|
rtc_vddsdio_set_config(cfg);
|
||||||
|
ets_delay_us(10); // wait for regulator to become stable
|
||||||
|
}
|
||||||
|
#endif // CONFIG_BOOTLOADER_VDDSDIO_BOOST
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FLASH_CLK_IO 6
|
||||||
|
#define FLASH_CS_IO 11
|
||||||
|
#define FLASH_SPIQ_IO 7
|
||||||
|
#define FLASH_SPID_IO 8
|
||||||
|
#define FLASH_SPIWP_IO 10
|
||||||
|
#define FLASH_SPIHD_IO 9
|
||||||
|
#define FLASH_IO_MATRIX_DUMMY_40M 1
|
||||||
|
#define FLASH_IO_MATRIX_DUMMY_80M 2
|
||||||
|
#define FLASH_IO_DRIVE_GD_WITH_1V8PSRAM 3
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bootloader reads SPI configuration from bin header, so that
|
||||||
|
* the burning configuration can be different with compiling configuration.
|
||||||
|
*/
|
||||||
|
static void IRAM_ATTR flash_gpio_configure(const esp_image_header_t* pfhdr)
|
||||||
|
{
|
||||||
|
int spi_cache_dummy = 0;
|
||||||
|
int drv = 2;
|
||||||
|
switch (pfhdr->spi_mode) {
|
||||||
|
case ESP_IMAGE_SPI_MODE_QIO:
|
||||||
|
spi_cache_dummy = SPI0_R_DIO_DUMMY_CYCLELEN;
|
||||||
|
break;
|
||||||
|
case ESP_IMAGE_SPI_MODE_DIO:
|
||||||
|
spi_cache_dummy = SPI0_R_DIO_DUMMY_CYCLELEN; //qio 3
|
||||||
|
break;
|
||||||
|
case ESP_IMAGE_SPI_MODE_QOUT:
|
||||||
|
case ESP_IMAGE_SPI_MODE_DOUT:
|
||||||
|
default:
|
||||||
|
spi_cache_dummy = SPI0_R_FAST_DUMMY_CYCLELEN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dummy_len_plus values defined in ROM for SPI flash configuration */
|
||||||
|
extern uint8_t g_rom_spiflash_dummy_len_plus[];
|
||||||
|
switch (pfhdr->spi_speed) {
|
||||||
|
case ESP_IMAGE_SPI_SPEED_80M:
|
||||||
|
g_rom_spiflash_dummy_len_plus[0] = FLASH_IO_MATRIX_DUMMY_80M;
|
||||||
|
g_rom_spiflash_dummy_len_plus[1] = FLASH_IO_MATRIX_DUMMY_80M;
|
||||||
|
SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, spi_cache_dummy + FLASH_IO_MATRIX_DUMMY_80M,
|
||||||
|
SPI_USR_DUMMY_CYCLELEN_S); //DUMMY
|
||||||
|
drv = 3;
|
||||||
|
break;
|
||||||
|
case ESP_IMAGE_SPI_SPEED_40M:
|
||||||
|
g_rom_spiflash_dummy_len_plus[0] = FLASH_IO_MATRIX_DUMMY_40M;
|
||||||
|
g_rom_spiflash_dummy_len_plus[1] = FLASH_IO_MATRIX_DUMMY_40M;
|
||||||
|
SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, spi_cache_dummy + FLASH_IO_MATRIX_DUMMY_40M,
|
||||||
|
SPI_USR_DUMMY_CYCLELEN_S); //DUMMY
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG);
|
||||||
|
uint32_t pkg_ver = chip_ver & 0x7;
|
||||||
|
|
||||||
|
if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5) {
|
||||||
|
// For ESP32D2WD the SPI pins are already configured
|
||||||
|
// flash clock signal should come from IO MUX.
|
||||||
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK);
|
||||||
|
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S);
|
||||||
|
} else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2) {
|
||||||
|
// For ESP32PICOD2 the SPI pins are already configured
|
||||||
|
// flash clock signal should come from IO MUX.
|
||||||
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK);
|
||||||
|
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S);
|
||||||
|
} else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) {
|
||||||
|
// For ESP32PICOD4 the SPI pins are already configured
|
||||||
|
// flash clock signal should come from IO MUX.
|
||||||
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK);
|
||||||
|
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S);
|
||||||
|
} else {
|
||||||
|
const uint32_t spiconfig = ets_efuse_get_spiconfig();
|
||||||
|
if (spiconfig == EFUSE_SPICONFIG_SPI_DEFAULTS) {
|
||||||
|
gpio_matrix_out(FLASH_CS_IO, SPICS0_OUT_IDX, 0, 0);
|
||||||
|
gpio_matrix_out(FLASH_SPIQ_IO, SPIQ_OUT_IDX, 0, 0);
|
||||||
|
gpio_matrix_in(FLASH_SPIQ_IO, SPIQ_IN_IDX, 0);
|
||||||
|
gpio_matrix_out(FLASH_SPID_IO, SPID_OUT_IDX, 0, 0);
|
||||||
|
gpio_matrix_in(FLASH_SPID_IO, SPID_IN_IDX, 0);
|
||||||
|
gpio_matrix_out(FLASH_SPIWP_IO, SPIWP_OUT_IDX, 0, 0);
|
||||||
|
gpio_matrix_in(FLASH_SPIWP_IO, SPIWP_IN_IDX, 0);
|
||||||
|
gpio_matrix_out(FLASH_SPIHD_IO, SPIHD_OUT_IDX, 0, 0);
|
||||||
|
gpio_matrix_in(FLASH_SPIHD_IO, SPIHD_IN_IDX, 0);
|
||||||
|
//select pin function gpio
|
||||||
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, PIN_FUNC_GPIO);
|
||||||
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, PIN_FUNC_GPIO);
|
||||||
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA2_U, PIN_FUNC_GPIO);
|
||||||
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA3_U, PIN_FUNC_GPIO);
|
||||||
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, PIN_FUNC_GPIO);
|
||||||
|
// flash clock signal should come from IO MUX.
|
||||||
|
// set drive ability for clock
|
||||||
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK);
|
||||||
|
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, drv, FUN_DRV_S);
|
||||||
|
|
||||||
|
#if CONFIG_SPIRAM_TYPE_ESPPSRAM32
|
||||||
|
uint32_t flash_id = g_rom_flashchip.device_id;
|
||||||
|
if (flash_id == FLASH_ID_GD25LQ32C) {
|
||||||
|
// Set drive ability for 1.8v flash in 80Mhz.
|
||||||
|
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_DATA0_U, FUN_DRV, 3, FUN_DRV_S);
|
||||||
|
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_DATA1_U, FUN_DRV, 3, FUN_DRV_S);
|
||||||
|
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_DATA2_U, FUN_DRV, 3, FUN_DRV_S);
|
||||||
|
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_DATA3_U, FUN_DRV, 3, FUN_DRV_S);
|
||||||
|
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CMD_U, FUN_DRV, 3, FUN_DRV_S);
|
||||||
|
SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, 3, FUN_DRV_S);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uart_console_configure(void)
|
||||||
|
{
|
||||||
|
#if CONFIG_CONSOLE_UART_NONE
|
||||||
|
ets_install_putc1(NULL);
|
||||||
|
ets_install_putc2(NULL);
|
||||||
|
#else // CONFIG_CONSOLE_UART_NONE
|
||||||
|
const int uart_num = CONFIG_CONSOLE_UART_NUM;
|
||||||
|
|
||||||
|
uartAttach();
|
||||||
|
ets_install_uart_printf();
|
||||||
|
|
||||||
|
// Wait for UART FIFO to be empty.
|
||||||
|
uart_tx_wait_idle(0);
|
||||||
|
|
||||||
|
#if CONFIG_CONSOLE_UART_CUSTOM
|
||||||
|
// Some constants to make the following code less upper-case
|
||||||
|
const int uart_tx_gpio = CONFIG_CONSOLE_UART_TX_GPIO;
|
||||||
|
const int uart_rx_gpio = CONFIG_CONSOLE_UART_RX_GPIO;
|
||||||
|
// Switch to the new UART (this just changes UART number used for
|
||||||
|
// ets_printf in ROM code).
|
||||||
|
uart_tx_switch(uart_num);
|
||||||
|
// If console is attached to UART1 or if non-default pins are used,
|
||||||
|
// need to reconfigure pins using GPIO matrix
|
||||||
|
if (uart_num != 0 || uart_tx_gpio != 1 || uart_rx_gpio != 3) {
|
||||||
|
// Change pin mode for GPIO1/3 from UART to GPIO
|
||||||
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_GPIO3);
|
||||||
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_GPIO1);
|
||||||
|
// Route GPIO signals to/from pins
|
||||||
|
// (arrays should be optimized away by the compiler)
|
||||||
|
const uint32_t tx_idx_list[3] = { U0TXD_OUT_IDX, U1TXD_OUT_IDX, U2TXD_OUT_IDX };
|
||||||
|
const uint32_t rx_idx_list[3] = { U0RXD_IN_IDX, U1RXD_IN_IDX, U2RXD_IN_IDX };
|
||||||
|
const uint32_t tx_idx = tx_idx_list[uart_num];
|
||||||
|
const uint32_t rx_idx = rx_idx_list[uart_num];
|
||||||
|
gpio_matrix_out(uart_tx_gpio, tx_idx, 0, 0);
|
||||||
|
gpio_matrix_in(uart_rx_gpio, rx_idx, 0);
|
||||||
|
}
|
||||||
|
#endif // CONFIG_CONSOLE_UART_CUSTOM
|
||||||
|
|
||||||
|
// Set configured UART console baud rate
|
||||||
|
const int uart_baud = CONFIG_CONSOLE_UART_BAUDRATE;
|
||||||
|
uart_div_modify(uart_num, (rtc_clk_apb_freq_get() << 4) / uart_baud);
|
||||||
|
|
||||||
|
#endif // CONFIG_CONSOLE_UART_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wdt_reset_cpu0_info_enable(void)
|
||||||
|
{
|
||||||
|
//We do not reset core1 info here because it didn't work before cpu1 was up. So we put it into call_start_cpu1.
|
||||||
|
DPORT_REG_SET_BIT(DPORT_PRO_CPU_RECORD_CTRL_REG, DPORT_PRO_CPU_PDEBUG_ENABLE | DPORT_PRO_CPU_RECORD_ENABLE);
|
||||||
|
DPORT_REG_CLR_BIT(DPORT_PRO_CPU_RECORD_CTRL_REG, DPORT_PRO_CPU_RECORD_ENABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wdt_reset_info_dump(int cpu)
|
||||||
|
{
|
||||||
|
uint32_t inst = 0, pid = 0, stat = 0, data = 0, pc = 0,
|
||||||
|
lsstat = 0, lsaddr = 0, lsdata = 0, dstat = 0;
|
||||||
|
const char *cpu_name = cpu ? "APP" : "PRO";
|
||||||
|
|
||||||
|
if (cpu == 0) {
|
||||||
|
stat = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_STATUS_REG);
|
||||||
|
pid = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_PID_REG);
|
||||||
|
inst = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_PDEBUGINST_REG);
|
||||||
|
dstat = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_PDEBUGSTATUS_REG);
|
||||||
|
data = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_PDEBUGDATA_REG);
|
||||||
|
pc = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_PDEBUGPC_REG);
|
||||||
|
lsstat = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_PDEBUGLS0STAT_REG);
|
||||||
|
lsaddr = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_PDEBUGLS0ADDR_REG);
|
||||||
|
lsdata = DPORT_REG_READ(DPORT_PRO_CPU_RECORD_PDEBUGLS0DATA_REG);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
stat = DPORT_REG_READ(DPORT_APP_CPU_RECORD_STATUS_REG);
|
||||||
|
pid = DPORT_REG_READ(DPORT_APP_CPU_RECORD_PID_REG);
|
||||||
|
inst = DPORT_REG_READ(DPORT_APP_CPU_RECORD_PDEBUGINST_REG);
|
||||||
|
dstat = DPORT_REG_READ(DPORT_APP_CPU_RECORD_PDEBUGSTATUS_REG);
|
||||||
|
data = DPORT_REG_READ(DPORT_APP_CPU_RECORD_PDEBUGDATA_REG);
|
||||||
|
pc = DPORT_REG_READ(DPORT_APP_CPU_RECORD_PDEBUGPC_REG);
|
||||||
|
lsstat = DPORT_REG_READ(DPORT_APP_CPU_RECORD_PDEBUGLS0STAT_REG);
|
||||||
|
lsaddr = DPORT_REG_READ(DPORT_APP_CPU_RECORD_PDEBUGLS0ADDR_REG);
|
||||||
|
lsdata = DPORT_REG_READ(DPORT_APP_CPU_RECORD_PDEBUGLS0DATA_REG);
|
||||||
|
}
|
||||||
|
if (DPORT_RECORD_PDEBUGINST_SZ(inst) == 0 &&
|
||||||
|
DPORT_RECORD_PDEBUGSTATUS_BBCAUSE(dstat) == DPORT_RECORD_PDEBUGSTATUS_BBCAUSE_WAITI) {
|
||||||
|
ESP_LOGW(TAG, "WDT reset info: %s CPU PC=0x%x (waiti mode)", cpu_name, pc);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "WDT reset info: %s CPU PC=0x%x", cpu_name, pc);
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "WDT reset info: %s CPU STATUS 0x%08x", cpu_name, stat);
|
||||||
|
ESP_LOGD(TAG, "WDT reset info: %s CPU PID 0x%08x", cpu_name, pid);
|
||||||
|
ESP_LOGD(TAG, "WDT reset info: %s CPU PDEBUGINST 0x%08x", cpu_name, inst);
|
||||||
|
ESP_LOGD(TAG, "WDT reset info: %s CPU PDEBUGSTATUS 0x%08x", cpu_name, dstat);
|
||||||
|
ESP_LOGD(TAG, "WDT reset info: %s CPU PDEBUGDATA 0x%08x", cpu_name, data);
|
||||||
|
ESP_LOGD(TAG, "WDT reset info: %s CPU PDEBUGPC 0x%08x", cpu_name, pc);
|
||||||
|
ESP_LOGD(TAG, "WDT reset info: %s CPU PDEBUGLS0STAT 0x%08x", cpu_name, lsstat);
|
||||||
|
ESP_LOGD(TAG, "WDT reset info: %s CPU PDEBUGLS0ADDR 0x%08x", cpu_name, lsaddr);
|
||||||
|
ESP_LOGD(TAG, "WDT reset info: %s CPU PDEBUGLS0DATA 0x%08x", cpu_name, lsdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wdt_reset_check(void)
|
||||||
|
{
|
||||||
|
int wdt_rst = 0;
|
||||||
|
RESET_REASON rst_reas[2];
|
||||||
|
|
||||||
|
rst_reas[0] = rtc_get_reset_reason(0);
|
||||||
|
rst_reas[1] = rtc_get_reset_reason(1);
|
||||||
|
if (rst_reas[0] == RTCWDT_SYS_RESET || rst_reas[0] == TG0WDT_SYS_RESET || rst_reas[0] == TG1WDT_SYS_RESET ||
|
||||||
|
rst_reas[0] == TGWDT_CPU_RESET || rst_reas[0] == RTCWDT_CPU_RESET) {
|
||||||
|
ESP_LOGW(TAG, "PRO CPU has been reset by WDT.");
|
||||||
|
wdt_rst = 1;
|
||||||
|
}
|
||||||
|
if (rst_reas[1] == RTCWDT_SYS_RESET || rst_reas[1] == TG0WDT_SYS_RESET || rst_reas[1] == TG1WDT_SYS_RESET ||
|
||||||
|
rst_reas[1] == TGWDT_CPU_RESET || rst_reas[1] == RTCWDT_CPU_RESET) {
|
||||||
|
ESP_LOGW(TAG, "APP CPU has been reset by WDT.");
|
||||||
|
wdt_rst = 1;
|
||||||
|
}
|
||||||
|
if (wdt_rst) {
|
||||||
|
// if reset by WDT dump info from trace port
|
||||||
|
wdt_reset_info_dump(0);
|
||||||
|
wdt_reset_info_dump(1);
|
||||||
|
}
|
||||||
|
wdt_reset_cpu0_info_enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void __assert_func(const char *file, int line, const char *func, const char *expr)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Assert failed in %s, %s:%d (%s)", func, file, line, expr);
|
||||||
|
while(1) {}
|
||||||
|
}
|
145
components/bootloader_support/src/bootloader_random.c
Normal file
145
components/bootloader_support/src/bootloader_random.c
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include "bootloader_random.h"
|
||||||
|
#include "soc/cpu.h"
|
||||||
|
#include "soc/wdev_reg.h"
|
||||||
|
#include "soc/rtc_cntl_reg.h"
|
||||||
|
#include "soc/sens_reg.h"
|
||||||
|
#include "soc/syscon_reg.h"
|
||||||
|
#include "soc/dport_reg.h"
|
||||||
|
#include "soc/i2s_reg.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#ifndef BOOTLOADER_BUILD
|
||||||
|
#include "esp_system.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void bootloader_fill_random(void *buffer, size_t length)
|
||||||
|
{
|
||||||
|
uint8_t *buffer_bytes = (uint8_t *)buffer;
|
||||||
|
uint32_t random;
|
||||||
|
#ifdef BOOTLOADER_BUILD
|
||||||
|
uint32_t start, now;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
if (i == 0 || i % 4 == 0) { /* redundant check is for a compiler warning */
|
||||||
|
#ifdef BOOTLOADER_BUILD
|
||||||
|
/* in bootloader with ADC feeding HWRNG, we accumulate 1
|
||||||
|
bit of entropy per 40 APB cycles (==80 CPU cycles.)
|
||||||
|
|
||||||
|
To avoid reading the entire RNG hardware state out
|
||||||
|
as-is, we repeatedly read the RNG register and XOR all
|
||||||
|
values.
|
||||||
|
*/
|
||||||
|
random = REG_READ(WDEV_RND_REG);
|
||||||
|
RSR(CCOUNT, start);
|
||||||
|
do {
|
||||||
|
random ^= REG_READ(WDEV_RND_REG);
|
||||||
|
RSR(CCOUNT, now);
|
||||||
|
} while(now - start < 80*32*2); /* extra factor of 2 is precautionary */
|
||||||
|
#else
|
||||||
|
random = esp_random();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_bytes[i] = random >> ((i % 4) * 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bootloader_random_enable(void)
|
||||||
|
{
|
||||||
|
/* Ensure the hardware RNG is enabled following a soft reset. This should always be the case already (this clock is
|
||||||
|
never disabled while the CPU is running), this is a "belts and braces" type check.
|
||||||
|
*/
|
||||||
|
DPORT_SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, DPORT_WIFI_CLK_RNG_EN);
|
||||||
|
|
||||||
|
/* Enable SAR ADC in test mode to feed ADC readings of the 1.1V
|
||||||
|
reference via I2S into the RNG entropy input.
|
||||||
|
|
||||||
|
Note: I2S requires the PLL to be running, so the call to rtc_set_cpu_freq(CPU_80M)
|
||||||
|
in early bootloader startup must have been made.
|
||||||
|
*/
|
||||||
|
SET_PERI_REG_BITS(RTC_CNTL_TEST_MUX_REG, RTC_CNTL_DTEST_RTC, 2, RTC_CNTL_DTEST_RTC_S);
|
||||||
|
SET_PERI_REG_MASK(RTC_CNTL_TEST_MUX_REG, RTC_CNTL_ENT_RTC);
|
||||||
|
SET_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_SAR2_EN_TEST);
|
||||||
|
|
||||||
|
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN);
|
||||||
|
CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_FORCE_START_TOP);
|
||||||
|
CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_ULP_CP_START_TOP);
|
||||||
|
// Test pattern configuration byte 0xAD:
|
||||||
|
//--[7:4] channel_sel: 10-->en_test
|
||||||
|
//--[3:2] bit_width : 3-->12bit
|
||||||
|
//--[1:0] atten : 1-->3dB attenuation
|
||||||
|
WRITE_PERI_REG(SYSCON_SARADC_SAR2_PATT_TAB1_REG, 0xADADADAD);
|
||||||
|
WRITE_PERI_REG(SYSCON_SARADC_SAR2_PATT_TAB2_REG, 0xADADADAD);
|
||||||
|
WRITE_PERI_REG(SYSCON_SARADC_SAR2_PATT_TAB3_REG, 0xADADADAD);
|
||||||
|
WRITE_PERI_REG(SYSCON_SARADC_SAR2_PATT_TAB4_REG, 0xADADADAD);
|
||||||
|
|
||||||
|
SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 3, SENS_FORCE_XPD_SAR_S);
|
||||||
|
SET_PERI_REG_MASK(SENS_SAR_READ_CTRL_REG, SENS_SAR1_DIG_FORCE);
|
||||||
|
SET_PERI_REG_MASK(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_DIG_FORCE);
|
||||||
|
SET_PERI_REG_MASK(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_SAR2_MUX);
|
||||||
|
SET_PERI_REG_BITS(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_SAR_CLK_DIV, 4, SYSCON_SARADC_SAR_CLK_DIV_S);
|
||||||
|
|
||||||
|
SET_PERI_REG_BITS(SYSCON_SARADC_FSM_REG, SYSCON_SARADC_RSTB_WAIT, 8, SYSCON_SARADC_RSTB_WAIT_S); /* was 1 */
|
||||||
|
SET_PERI_REG_BITS(SYSCON_SARADC_FSM_REG, SYSCON_SARADC_START_WAIT, 10, SYSCON_SARADC_START_WAIT_S);
|
||||||
|
SET_PERI_REG_BITS(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_WORK_MODE, 0, SYSCON_SARADC_WORK_MODE_S);
|
||||||
|
SET_PERI_REG_MASK(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_SAR_SEL);
|
||||||
|
CLEAR_PERI_REG_MASK(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_DATA_SAR_SEL);
|
||||||
|
|
||||||
|
SET_PERI_REG_BITS(I2S_SAMPLE_RATE_CONF_REG(0), I2S_RX_BCK_DIV_NUM, 20, I2S_RX_BCK_DIV_NUM_S);
|
||||||
|
|
||||||
|
SET_PERI_REG_MASK(SYSCON_SARADC_CTRL_REG,SYSCON_SARADC_DATA_TO_I2S);
|
||||||
|
|
||||||
|
CLEAR_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_CAMERA_EN);
|
||||||
|
SET_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_LCD_EN);
|
||||||
|
SET_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_DATA_ENABLE);
|
||||||
|
SET_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_DATA_ENABLE_TEST_EN);
|
||||||
|
SET_PERI_REG_MASK(I2S_CONF_REG(0), I2S_RX_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bootloader_random_disable(void)
|
||||||
|
{
|
||||||
|
/* Disable i2s clock */
|
||||||
|
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_I2S0_CLK_EN);
|
||||||
|
|
||||||
|
|
||||||
|
/* Reset some i2s configuration (possibly redundant as we reset entire
|
||||||
|
I2S peripheral further down). */
|
||||||
|
CLEAR_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_CAMERA_EN);
|
||||||
|
CLEAR_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_LCD_EN);
|
||||||
|
CLEAR_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_DATA_ENABLE_TEST_EN);
|
||||||
|
CLEAR_PERI_REG_MASK(I2S_CONF2_REG(0), I2S_DATA_ENABLE);
|
||||||
|
CLEAR_PERI_REG_MASK(I2S_CONF_REG(0), I2S_RX_START);
|
||||||
|
|
||||||
|
/* Restore SYSCON mode registers */
|
||||||
|
CLEAR_PERI_REG_MASK(SENS_SAR_READ_CTRL_REG, SENS_SAR1_DIG_FORCE);
|
||||||
|
CLEAR_PERI_REG_MASK(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_DIG_FORCE);
|
||||||
|
|
||||||
|
/* Restore SAR ADC mode */
|
||||||
|
CLEAR_PERI_REG_MASK(SENS_SAR_START_FORCE_REG, SENS_SAR2_EN_TEST);
|
||||||
|
CLEAR_PERI_REG_MASK(SYSCON_SARADC_CTRL_REG, SYSCON_SARADC_SAR2_MUX
|
||||||
|
| SYSCON_SARADC_SAR_SEL | SYSCON_SARADC_DATA_TO_I2S);
|
||||||
|
SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR, 0, SENS_FORCE_XPD_SAR_S);
|
||||||
|
SET_PERI_REG_BITS(SYSCON_SARADC_FSM_REG, SYSCON_SARADC_START_WAIT, 8, SYSCON_SARADC_START_WAIT_S);
|
||||||
|
|
||||||
|
/* Reset i2s peripheral */
|
||||||
|
DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST);
|
||||||
|
DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_I2S0_RST);
|
||||||
|
|
||||||
|
/* Disable pull supply voltage to SAR ADC */
|
||||||
|
CLEAR_PERI_REG_MASK(RTC_CNTL_TEST_MUX_REG, RTC_CNTL_ENT_RTC);
|
||||||
|
SET_PERI_REG_BITS(RTC_CNTL_TEST_MUX_REG, RTC_CNTL_DTEST_RTC, 0, RTC_CNTL_DTEST_RTC_S);
|
||||||
|
}
|
166
components/bootloader_support/src/bootloader_sha.c
Normal file
166
components/bootloader_support/src/bootloader_sha.c
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include "bootloader_sha.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
|
||||||
|
#ifndef BOOTLOADER_BUILD
|
||||||
|
// App version is a wrapper around mbedTLS SHA API
|
||||||
|
#include <mbedtls/sha256.h>
|
||||||
|
|
||||||
|
bootloader_sha256_handle_t bootloader_sha256_start()
|
||||||
|
{
|
||||||
|
mbedtls_sha256_context *ctx = (mbedtls_sha256_context *)malloc(sizeof(mbedtls_sha256_context));
|
||||||
|
if (!ctx) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
mbedtls_sha256_init(ctx);
|
||||||
|
assert(mbedtls_sha256_starts_ret(ctx, false) == 0);
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bootloader_sha256_data(bootloader_sha256_handle_t handle, const void *data, size_t data_len)
|
||||||
|
{
|
||||||
|
assert(handle != NULL);
|
||||||
|
mbedtls_sha256_context *ctx = (mbedtls_sha256_context *)handle;
|
||||||
|
assert(mbedtls_sha256_update_ret(ctx, data, data_len) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bootloader_sha256_finish(bootloader_sha256_handle_t handle, uint8_t *digest)
|
||||||
|
{
|
||||||
|
assert(handle != NULL);
|
||||||
|
mbedtls_sha256_context *ctx = (mbedtls_sha256_context *)handle;
|
||||||
|
if (digest != NULL) {
|
||||||
|
assert(mbedtls_sha256_finish_ret(ctx, digest) == 0);
|
||||||
|
}
|
||||||
|
mbedtls_sha256_free(ctx);
|
||||||
|
free(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // Bootloader version
|
||||||
|
|
||||||
|
#include "rom/sha.h"
|
||||||
|
#include "soc/dport_reg.h"
|
||||||
|
#include "soc/hwcrypto_reg.h"
|
||||||
|
|
||||||
|
#include "rom/ets_sys.h" // TO REMOVE
|
||||||
|
|
||||||
|
static uint32_t words_hashed;
|
||||||
|
|
||||||
|
// Words per SHA256 block
|
||||||
|
static const size_t BLOCK_WORDS = (64/sizeof(uint32_t));
|
||||||
|
// Words in final SHA256 digest
|
||||||
|
static const size_t DIGEST_WORDS = (32/sizeof(uint32_t));
|
||||||
|
|
||||||
|
bootloader_sha256_handle_t bootloader_sha256_start()
|
||||||
|
{
|
||||||
|
// Enable SHA hardware
|
||||||
|
ets_sha_enable();
|
||||||
|
words_hashed = 0;
|
||||||
|
return (bootloader_sha256_handle_t)&words_hashed; // Meaningless non-NULL value
|
||||||
|
}
|
||||||
|
|
||||||
|
void bootloader_sha256_data(bootloader_sha256_handle_t handle, const void *data, size_t data_len)
|
||||||
|
{
|
||||||
|
assert(handle != NULL);
|
||||||
|
assert(data_len % 4 == 0);
|
||||||
|
|
||||||
|
const uint32_t *w = (const uint32_t *)data;
|
||||||
|
size_t word_len = data_len / 4;
|
||||||
|
uint32_t *sha_text_reg = (uint32_t *)(SHA_TEXT_BASE);
|
||||||
|
|
||||||
|
//ets_printf("word_len %d so far %d\n", word_len, words_hashed);
|
||||||
|
while (word_len > 0) {
|
||||||
|
size_t block_count = words_hashed % BLOCK_WORDS;
|
||||||
|
size_t copy_words = (BLOCK_WORDS - block_count);
|
||||||
|
|
||||||
|
copy_words = MIN(word_len, copy_words);
|
||||||
|
|
||||||
|
// Wait for SHA engine idle
|
||||||
|
while(REG_READ(SHA_256_BUSY_REG) != 0) { }
|
||||||
|
|
||||||
|
// Copy to memory block
|
||||||
|
//ets_printf("block_count %d copy_words %d\n", block_count, copy_words);
|
||||||
|
for (int i = 0; i < copy_words; i++) {
|
||||||
|
sha_text_reg[block_count + i] = __builtin_bswap32(w[i]);
|
||||||
|
}
|
||||||
|
asm volatile ("memw");
|
||||||
|
|
||||||
|
// Update counters
|
||||||
|
words_hashed += copy_words;
|
||||||
|
block_count += copy_words;
|
||||||
|
word_len -= copy_words;
|
||||||
|
w += copy_words;
|
||||||
|
|
||||||
|
// If we loaded a full block, run the SHA engine
|
||||||
|
if (block_count == BLOCK_WORDS) {
|
||||||
|
//ets_printf("running engine @ count %d\n", words_hashed);
|
||||||
|
if (words_hashed == BLOCK_WORDS) {
|
||||||
|
REG_WRITE(SHA_256_START_REG, 1);
|
||||||
|
} else {
|
||||||
|
REG_WRITE(SHA_256_CONTINUE_REG, 1);
|
||||||
|
}
|
||||||
|
block_count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bootloader_sha256_finish(bootloader_sha256_handle_t handle, uint8_t *digest)
|
||||||
|
{
|
||||||
|
assert(handle != NULL);
|
||||||
|
|
||||||
|
if (digest == NULL) {
|
||||||
|
return; // We'd free resources here, but there are none to free
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t data_words = words_hashed;
|
||||||
|
|
||||||
|
// Pad to a 55 byte long block loaded in the engine
|
||||||
|
// (leaving 1 byte 0x80 plus variable padding plus 8 bytes of length,
|
||||||
|
// to fill a 64 byte block.)
|
||||||
|
int block_bytes = (words_hashed % BLOCK_WORDS) * 4;
|
||||||
|
int pad_bytes = 55 - block_bytes;
|
||||||
|
if (pad_bytes < 0) {
|
||||||
|
pad_bytes += 64;
|
||||||
|
}
|
||||||
|
static const uint8_t padding[64] = { 0x80, 0, };
|
||||||
|
|
||||||
|
pad_bytes += 5; // 1 byte for 0x80 plus first 4 bytes of the 64-bit length
|
||||||
|
assert(pad_bytes % 4 == 0); // should be, as (block_bytes % 4 == 0)
|
||||||
|
|
||||||
|
bootloader_sha256_data(handle, padding, pad_bytes);
|
||||||
|
|
||||||
|
assert(words_hashed % BLOCK_WORDS == 60/4); // 32-bits left in block
|
||||||
|
|
||||||
|
// Calculate 32-bit length for final 32 bits of data
|
||||||
|
uint32_t bit_count = __builtin_bswap32( data_words * 32 );
|
||||||
|
bootloader_sha256_data(handle, &bit_count, sizeof(bit_count));
|
||||||
|
|
||||||
|
assert(words_hashed % BLOCK_WORDS == 0);
|
||||||
|
|
||||||
|
while(REG_READ(SHA_256_BUSY_REG) == 1) { }
|
||||||
|
REG_WRITE(SHA_256_LOAD_REG, 1);
|
||||||
|
while(REG_READ(SHA_256_BUSY_REG) == 1) { }
|
||||||
|
|
||||||
|
uint32_t *digest_words = (uint32_t *)digest;
|
||||||
|
uint32_t *sha_text_reg = (uint32_t *)(SHA_TEXT_BASE);
|
||||||
|
for (int i = 0; i < DIGEST_WORDS; i++) {
|
||||||
|
digest_words[i] = __builtin_bswap32(sha_text_reg[i]);
|
||||||
|
}
|
||||||
|
asm volatile ("memw");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
472
components/bootloader_support/src/bootloader_utility.c
Normal file
472
components/bootloader_support/src/bootloader_utility.c
Normal file
@ -0,0 +1,472 @@
|
|||||||
|
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
|
||||||
|
#include "esp_attr.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#include "rom/cache.h"
|
||||||
|
#include "rom/efuse.h"
|
||||||
|
#include "rom/ets_sys.h"
|
||||||
|
#include "rom/spi_flash.h"
|
||||||
|
#include "rom/crc.h"
|
||||||
|
#include "rom/rtc.h"
|
||||||
|
#include "rom/uart.h"
|
||||||
|
#include "rom/gpio.h"
|
||||||
|
#include "rom/secure_boot.h"
|
||||||
|
|
||||||
|
#include "soc/soc.h"
|
||||||
|
#include "soc/cpu.h"
|
||||||
|
#include "soc/rtc.h"
|
||||||
|
#include "soc/dport_reg.h"
|
||||||
|
#include "soc/io_mux_reg.h"
|
||||||
|
#include "soc/efuse_reg.h"
|
||||||
|
#include "soc/rtc_cntl_reg.h"
|
||||||
|
#include "soc/timer_group_reg.h"
|
||||||
|
#include "soc/gpio_reg.h"
|
||||||
|
#include "soc/gpio_sig_map.h"
|
||||||
|
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "esp_image_format.h"
|
||||||
|
#include "esp_secure_boot.h"
|
||||||
|
#include "esp_flash_encrypt.h"
|
||||||
|
#include "esp_flash_partitions.h"
|
||||||
|
#include "bootloader_flash.h"
|
||||||
|
#include "bootloader_random.h"
|
||||||
|
#include "bootloader_config.h"
|
||||||
|
#include "bootloader_common.h"
|
||||||
|
|
||||||
|
static const char* TAG = "boot";
|
||||||
|
|
||||||
|
/* Reduce literal size for some generic string literals */
|
||||||
|
#define MAP_ERR_MSG "Image contains multiple %s segments. Only the last one will be mapped."
|
||||||
|
|
||||||
|
static void unpack_load_app(const esp_image_metadata_t *data);
|
||||||
|
static void set_cache_and_start_app(uint32_t drom_addr,
|
||||||
|
uint32_t drom_load_addr,
|
||||||
|
uint32_t drom_size,
|
||||||
|
uint32_t irom_addr,
|
||||||
|
uint32_t irom_load_addr,
|
||||||
|
uint32_t irom_size,
|
||||||
|
uint32_t entry_addr);
|
||||||
|
|
||||||
|
bool bootloader_utility_load_partition_table(bootloader_state_t* bs)
|
||||||
|
{
|
||||||
|
const esp_partition_info_t *partitions;
|
||||||
|
const char *partition_usage;
|
||||||
|
esp_err_t err;
|
||||||
|
int num_partitions;
|
||||||
|
|
||||||
|
#ifdef CONFIG_SECURE_BOOT_ENABLED
|
||||||
|
if(esp_secure_boot_enabled()) {
|
||||||
|
ESP_LOGI(TAG, "Verifying partition table signature...");
|
||||||
|
err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_MAX_LEN);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to verify partition table signature.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Partition table signature verified");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
partitions = bootloader_mmap(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_MAX_LEN);
|
||||||
|
if (!partitions) {
|
||||||
|
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_MAX_LEN);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", ESP_PARTITION_TABLE_ADDR, (intptr_t)partitions);
|
||||||
|
|
||||||
|
err = esp_partition_table_basic_verify(partitions, true, &num_partitions);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to verify partition table");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Partition Table:");
|
||||||
|
ESP_LOGI(TAG, "## Label Usage Type ST Offset Length");
|
||||||
|
|
||||||
|
for(int i = 0; i < num_partitions; i++) {
|
||||||
|
const esp_partition_info_t *partition = &partitions[i];
|
||||||
|
ESP_LOGD(TAG, "load partition table entry 0x%x", (intptr_t)partition);
|
||||||
|
ESP_LOGD(TAG, "type=%x subtype=%x", partition->type, partition->subtype);
|
||||||
|
partition_usage = "unknown";
|
||||||
|
|
||||||
|
/* valid partition table */
|
||||||
|
switch(partition->type) {
|
||||||
|
case PART_TYPE_APP: /* app partition */
|
||||||
|
switch(partition->subtype) {
|
||||||
|
case PART_SUBTYPE_FACTORY: /* factory binary */
|
||||||
|
bs->factory = partition->pos;
|
||||||
|
partition_usage = "factory app";
|
||||||
|
break;
|
||||||
|
case PART_SUBTYPE_TEST: /* test binary */
|
||||||
|
bs->test = partition->pos;
|
||||||
|
partition_usage = "test app";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* OTA binary */
|
||||||
|
if ((partition->subtype & ~PART_SUBTYPE_OTA_MASK) == PART_SUBTYPE_OTA_FLAG) {
|
||||||
|
bs->ota[partition->subtype & PART_SUBTYPE_OTA_MASK] = partition->pos;
|
||||||
|
++bs->app_count;
|
||||||
|
partition_usage = "OTA app";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
partition_usage = "Unknown app";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break; /* PART_TYPE_APP */
|
||||||
|
case PART_TYPE_DATA: /* data partition */
|
||||||
|
switch(partition->subtype) {
|
||||||
|
case PART_SUBTYPE_DATA_OTA: /* ota data */
|
||||||
|
bs->ota_info = partition->pos;
|
||||||
|
partition_usage = "OTA data";
|
||||||
|
break;
|
||||||
|
case PART_SUBTYPE_DATA_RF:
|
||||||
|
partition_usage = "RF data";
|
||||||
|
break;
|
||||||
|
case PART_SUBTYPE_DATA_WIFI:
|
||||||
|
partition_usage = "WiFi data";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
partition_usage = "Unknown data";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break; /* PARTITION_USAGE_DATA */
|
||||||
|
default: /* other partition type */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* print partition type info */
|
||||||
|
ESP_LOGI(TAG, "%2d %-16s %-16s %02x %02x %08x %08x", i, partition->label, partition_usage,
|
||||||
|
partition->type, partition->subtype,
|
||||||
|
partition->pos.offset, partition->pos.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bootloader_munmap(partitions);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG,"End of partition table");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Given a partition index, return the partition position data from the bootloader_state_t structure */
|
||||||
|
static esp_partition_pos_t index_to_partition(const bootloader_state_t *bs, int index)
|
||||||
|
{
|
||||||
|
if (index == FACTORY_INDEX) {
|
||||||
|
return bs->factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == TEST_APP_INDEX) {
|
||||||
|
return bs->test;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index >= 0 && index < MAX_OTA_SLOTS && index < bs->app_count) {
|
||||||
|
return bs->ota[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_partition_pos_t invalid = { 0 };
|
||||||
|
return invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void log_invalid_app_partition(int index)
|
||||||
|
{
|
||||||
|
const char *not_bootable = " is not bootable"; /* save a few string literal bytes */
|
||||||
|
switch(index) {
|
||||||
|
case FACTORY_INDEX:
|
||||||
|
ESP_LOGE(TAG, "Factory app partition%s", not_bootable);
|
||||||
|
break;
|
||||||
|
case TEST_APP_INDEX:
|
||||||
|
ESP_LOGE(TAG, "Factory test app partition%s", not_bootable);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_LOGE(TAG, "OTA app partition slot %d%s", index, not_bootable);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int bootloader_utility_get_selected_boot_partition(const bootloader_state_t *bs)
|
||||||
|
{
|
||||||
|
esp_ota_select_entry_t sa,sb;
|
||||||
|
const esp_ota_select_entry_t *ota_select_map;
|
||||||
|
|
||||||
|
if (bs->ota_info.offset != 0) {
|
||||||
|
// partition table has OTA data partition
|
||||||
|
if (bs->ota_info.size < 2 * SPI_SEC_SIZE) {
|
||||||
|
ESP_LOGE(TAG, "ota_info partition size %d is too small (minimum %d bytes)", bs->ota_info.size, sizeof(esp_ota_select_entry_t));
|
||||||
|
return INVALID_INDEX; // can't proceed
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "OTA data offset 0x%x", bs->ota_info.offset);
|
||||||
|
ota_select_map = bootloader_mmap(bs->ota_info.offset, bs->ota_info.size);
|
||||||
|
if (!ota_select_map) {
|
||||||
|
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", bs->ota_info.offset, bs->ota_info.size);
|
||||||
|
return INVALID_INDEX; // can't proceed
|
||||||
|
}
|
||||||
|
memcpy(&sa, ota_select_map, sizeof(esp_ota_select_entry_t));
|
||||||
|
memcpy(&sb, (uint8_t *)ota_select_map + SPI_SEC_SIZE, sizeof(esp_ota_select_entry_t));
|
||||||
|
bootloader_munmap(ota_select_map);
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "OTA sequence values A 0x%08x B 0x%08x", sa.ota_seq, sb.ota_seq);
|
||||||
|
if(sa.ota_seq == UINT32_MAX && sb.ota_seq == UINT32_MAX) {
|
||||||
|
ESP_LOGD(TAG, "OTA sequence numbers both empty (all-0xFF)");
|
||||||
|
if (bs->factory.offset != 0) {
|
||||||
|
ESP_LOGI(TAG, "Defaulting to factory image");
|
||||||
|
return FACTORY_INDEX;
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "No factory image, trying OTA 0");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bool ota_valid = false;
|
||||||
|
const char *ota_msg;
|
||||||
|
int ota_seq; // Raw OTA sequence number. May be more than # of OTA slots
|
||||||
|
if(bootloader_common_ota_select_valid(&sa) && bootloader_common_ota_select_valid(&sb)) {
|
||||||
|
ota_valid = true;
|
||||||
|
ota_msg = "Both OTA values";
|
||||||
|
ota_seq = MAX(sa.ota_seq, sb.ota_seq) - 1;
|
||||||
|
} else if(bootloader_common_ota_select_valid(&sa)) {
|
||||||
|
ota_valid = true;
|
||||||
|
ota_msg = "Only OTA sequence A is";
|
||||||
|
ota_seq = sa.ota_seq - 1;
|
||||||
|
} else if(bootloader_common_ota_select_valid(&sb)) {
|
||||||
|
ota_valid = true;
|
||||||
|
ota_msg = "Only OTA sequence B is";
|
||||||
|
ota_seq = sb.ota_seq - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ota_valid) {
|
||||||
|
int ota_slot = ota_seq % bs->app_count; // Actual OTA partition selection
|
||||||
|
ESP_LOGD(TAG, "%s valid. Mapping seq %d -> OTA slot %d", ota_msg, ota_seq, ota_slot);
|
||||||
|
return ota_slot;
|
||||||
|
} else if (bs->factory.offset != 0) {
|
||||||
|
ESP_LOGE(TAG, "ota data partition invalid, falling back to factory");
|
||||||
|
return FACTORY_INDEX;
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "ota data partition invalid and no factory, will try all partitions");
|
||||||
|
return FACTORY_INDEX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, start from factory app partition and let the search logic
|
||||||
|
// proceed from there
|
||||||
|
return FACTORY_INDEX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return true if a partition has a valid app image that was successfully loaded */
|
||||||
|
static bool try_load_partition(const esp_partition_pos_t *partition, esp_image_metadata_t *data)
|
||||||
|
{
|
||||||
|
if (partition->size == 0) {
|
||||||
|
ESP_LOGD(TAG, "Can't boot from zero-length partition");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#ifdef BOOTLOADER_BUILD
|
||||||
|
if (esp_image_load(ESP_IMAGE_LOAD, partition, data) == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "Loaded app from partition at offset 0x%x",
|
||||||
|
partition->offset);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TRY_LOG_FORMAT "Trying partition index %d offs 0x%x size 0x%x"
|
||||||
|
|
||||||
|
bool bootloader_utility_load_boot_image(const bootloader_state_t *bs, int start_index, esp_image_metadata_t *result)
|
||||||
|
{
|
||||||
|
int index = start_index;
|
||||||
|
esp_partition_pos_t part;
|
||||||
|
if(start_index == TEST_APP_INDEX) {
|
||||||
|
if (try_load_partition(&bs->test, result)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "No bootable test partition in the partition table");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* work backwards from start_index, down to the factory app */
|
||||||
|
for(index = start_index; index >= FACTORY_INDEX; index--) {
|
||||||
|
part = index_to_partition(bs, index);
|
||||||
|
if (part.size == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, TRY_LOG_FORMAT, index, part.offset, part.size);
|
||||||
|
if (try_load_partition(&part, result)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
log_invalid_app_partition(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* failing that work forwards from start_index, try valid OTA slots */
|
||||||
|
for(index = start_index + 1; index < bs->app_count; index++) {
|
||||||
|
part = index_to_partition(bs, index);
|
||||||
|
if (part.size == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, TRY_LOG_FORMAT, index, part.offset, part.size);
|
||||||
|
if (try_load_partition(&part, result)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
log_invalid_app_partition(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (try_load_partition(&bs->test, result)) {
|
||||||
|
ESP_LOGW(TAG, "Falling back to test app as only bootable partition");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGE(TAG, "No bootable app partitions in the partition table");
|
||||||
|
bzero(result, sizeof(esp_image_metadata_t));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bootloader_utility_load_image(const esp_image_metadata_t* image_data)
|
||||||
|
{
|
||||||
|
#if defined(CONFIG_SECURE_BOOT_ENABLED) || defined(CONFIG_FLASH_ENCRYPTION_ENABLED)
|
||||||
|
esp_err_t err;
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_SECURE_BOOT_ENABLED
|
||||||
|
/* Generate secure digest from this bootloader to protect future
|
||||||
|
modifications */
|
||||||
|
ESP_LOGI(TAG, "Checking secure boot...");
|
||||||
|
err = esp_secure_boot_permanently_enable();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err);
|
||||||
|
/* Allow booting to continue, as the failure is probably
|
||||||
|
due to user-configured EFUSEs for testing...
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_FLASH_ENCRYPTION_ENABLED
|
||||||
|
/* encrypt flash */
|
||||||
|
ESP_LOGI(TAG, "Checking flash encryption...");
|
||||||
|
bool flash_encryption_enabled = esp_flash_encryption_enabled();
|
||||||
|
err = esp_flash_encrypt_check_and_update();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Flash encryption check failed (%d).", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!flash_encryption_enabled && esp_flash_encryption_enabled()) {
|
||||||
|
/* Flash encryption was just enabled for the first time,
|
||||||
|
so issue a system reset to ensure flash encryption
|
||||||
|
cache resets properly */
|
||||||
|
ESP_LOGI(TAG, "Resetting with flash encryption enabled...");
|
||||||
|
REG_WRITE(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_SYS_RST);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Disabling RNG early entropy source...");
|
||||||
|
bootloader_random_disable();
|
||||||
|
|
||||||
|
// copy loaded segments to RAM, set up caches for mapped segments, and start application
|
||||||
|
unpack_load_app(image_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unpack_load_app(const esp_image_metadata_t* data)
|
||||||
|
{
|
||||||
|
uint32_t drom_addr = 0;
|
||||||
|
uint32_t drom_load_addr = 0;
|
||||||
|
uint32_t drom_size = 0;
|
||||||
|
uint32_t irom_addr = 0;
|
||||||
|
uint32_t irom_load_addr = 0;
|
||||||
|
uint32_t irom_size = 0;
|
||||||
|
|
||||||
|
// Find DROM & IROM addresses, to configure cache mappings
|
||||||
|
for (int i = 0; i < data->image.segment_count; i++) {
|
||||||
|
const esp_image_segment_header_t *header = &data->segments[i];
|
||||||
|
if (header->load_addr >= SOC_IROM_LOW && header->load_addr < SOC_IROM_HIGH) {
|
||||||
|
if (drom_addr != 0) {
|
||||||
|
ESP_LOGE(TAG, MAP_ERR_MSG, "DROM");
|
||||||
|
} else {
|
||||||
|
ESP_LOGD(TAG, "Mapping segment %d as %s", i, "DROM");
|
||||||
|
}
|
||||||
|
drom_addr = data->segment_data[i];
|
||||||
|
drom_load_addr = header->load_addr;
|
||||||
|
drom_size = header->data_len;
|
||||||
|
}
|
||||||
|
if (header->load_addr >= SOC_DROM_LOW && header->load_addr < SOC_DROM_HIGH) {
|
||||||
|
if (irom_addr != 0) {
|
||||||
|
ESP_LOGE(TAG, MAP_ERR_MSG, "IROM");
|
||||||
|
} else {
|
||||||
|
ESP_LOGD(TAG, "Mapping segment %d as %s", i, "IROM");
|
||||||
|
}
|
||||||
|
irom_addr = data->segment_data[i];
|
||||||
|
irom_load_addr = header->load_addr;
|
||||||
|
irom_size = header->data_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "calling set_cache_and_start_app");
|
||||||
|
set_cache_and_start_app(drom_addr,
|
||||||
|
drom_load_addr,
|
||||||
|
drom_size,
|
||||||
|
irom_addr,
|
||||||
|
irom_load_addr,
|
||||||
|
irom_size,
|
||||||
|
data->image.entry_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_cache_and_start_app(
|
||||||
|
uint32_t drom_addr,
|
||||||
|
uint32_t drom_load_addr,
|
||||||
|
uint32_t drom_size,
|
||||||
|
uint32_t irom_addr,
|
||||||
|
uint32_t irom_load_addr,
|
||||||
|
uint32_t irom_size,
|
||||||
|
uint32_t entry_addr)
|
||||||
|
{
|
||||||
|
ESP_LOGD(TAG, "configure drom and irom and start");
|
||||||
|
Cache_Read_Disable( 0 );
|
||||||
|
Cache_Flush( 0 );
|
||||||
|
|
||||||
|
/* Clear the MMU entries that are already set up,
|
||||||
|
so the new app only has the mappings it creates.
|
||||||
|
*/
|
||||||
|
for (int i = 0; i < DPORT_FLASH_MMU_TABLE_SIZE; i++) {
|
||||||
|
DPORT_PRO_FLASH_MMU_TABLE[i] = DPORT_FLASH_MMU_TABLE_INVALID_VAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t drom_page_count = (drom_size + 64*1024 - 1) / (64*1024); // round up to 64k
|
||||||
|
ESP_LOGV(TAG, "d mmu set paddr=%08x vaddr=%08x size=%d n=%d", drom_addr & 0xffff0000, drom_load_addr & 0xffff0000, drom_size, drom_page_count );
|
||||||
|
int rc = cache_flash_mmu_set( 0, 0, drom_load_addr & 0xffff0000, drom_addr & 0xffff0000, 64, drom_page_count );
|
||||||
|
ESP_LOGV(TAG, "rc=%d", rc );
|
||||||
|
rc = cache_flash_mmu_set( 1, 0, drom_load_addr & 0xffff0000, drom_addr & 0xffff0000, 64, drom_page_count );
|
||||||
|
ESP_LOGV(TAG, "rc=%d", rc );
|
||||||
|
uint32_t irom_page_count = (irom_size + 64*1024 - 1) / (64*1024); // round up to 64k
|
||||||
|
ESP_LOGV(TAG, "i mmu set paddr=%08x vaddr=%08x size=%d n=%d", irom_addr & 0xffff0000, irom_load_addr & 0xffff0000, irom_size, irom_page_count );
|
||||||
|
rc = cache_flash_mmu_set( 0, 0, irom_load_addr & 0xffff0000, irom_addr & 0xffff0000, 64, irom_page_count );
|
||||||
|
ESP_LOGV(TAG, "rc=%d", rc );
|
||||||
|
rc = cache_flash_mmu_set( 1, 0, irom_load_addr & 0xffff0000, irom_addr & 0xffff0000, 64, irom_page_count );
|
||||||
|
ESP_LOGV(TAG, "rc=%d", rc );
|
||||||
|
DPORT_REG_CLR_BIT( DPORT_PRO_CACHE_CTRL1_REG, (DPORT_PRO_CACHE_MASK_IRAM0) | (DPORT_PRO_CACHE_MASK_IRAM1 & 0) | (DPORT_PRO_CACHE_MASK_IROM0 & 0) | DPORT_PRO_CACHE_MASK_DROM0 | DPORT_PRO_CACHE_MASK_DRAM1 );
|
||||||
|
DPORT_REG_CLR_BIT( DPORT_APP_CACHE_CTRL1_REG, (DPORT_APP_CACHE_MASK_IRAM0) | (DPORT_APP_CACHE_MASK_IRAM1 & 0) | (DPORT_APP_CACHE_MASK_IROM0 & 0) | DPORT_APP_CACHE_MASK_DROM0 | DPORT_APP_CACHE_MASK_DRAM1 );
|
||||||
|
Cache_Read_Enable( 0 );
|
||||||
|
|
||||||
|
// Application will need to do Cache_Flush(1) and Cache_Read_Enable(1)
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "start: 0x%08x", entry_addr);
|
||||||
|
typedef void (*entry_t)(void);
|
||||||
|
entry_t entry = ((entry_t) entry_addr);
|
||||||
|
|
||||||
|
// TODO: we have used quite a bit of stack at this point.
|
||||||
|
// use "movsp" instruction to reset stack back to where ROM stack starts.
|
||||||
|
(*entry)();
|
||||||
|
}
|
60
components/bootloader_support/src/efuse.c
Normal file
60
components/bootloader_support/src/efuse.c
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include "esp_efuse.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#define EFUSE_CONF_WRITE 0x5A5A /* efuse_pgm_op_ena, force no rd/wr disable */
|
||||||
|
#define EFUSE_CONF_READ 0x5AA5 /* efuse_read_op_ena, release force */
|
||||||
|
|
||||||
|
#define EFUSE_CMD_PGM 0x02
|
||||||
|
#define EFUSE_CMD_READ 0x01
|
||||||
|
|
||||||
|
static const char *TAG = "efuse";
|
||||||
|
|
||||||
|
void esp_efuse_burn_new_values(void)
|
||||||
|
{
|
||||||
|
REG_WRITE(EFUSE_CONF_REG, EFUSE_CONF_WRITE);
|
||||||
|
REG_WRITE(EFUSE_CMD_REG, EFUSE_CMD_PGM);
|
||||||
|
while (REG_READ(EFUSE_CMD_REG) != 0) {
|
||||||
|
}
|
||||||
|
REG_WRITE(EFUSE_CONF_REG, EFUSE_CONF_READ);
|
||||||
|
REG_WRITE(EFUSE_CMD_REG, EFUSE_CMD_READ);
|
||||||
|
while (REG_READ(EFUSE_CMD_REG) != 0) {
|
||||||
|
}
|
||||||
|
esp_efuse_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void esp_efuse_reset(void)
|
||||||
|
{
|
||||||
|
REG_WRITE(EFUSE_CONF_REG, EFUSE_CONF_READ);
|
||||||
|
const uint32_t block_start[4] = { EFUSE_BLK0_WDATA0_REG, EFUSE_BLK1_WDATA0_REG,
|
||||||
|
EFUSE_BLK2_WDATA0_REG, EFUSE_BLK3_WDATA0_REG };
|
||||||
|
const uint32_t block_end[4] = { EFUSE_BLK0_WDATA6_REG, EFUSE_BLK1_WDATA7_REG,
|
||||||
|
EFUSE_BLK2_WDATA7_REG, EFUSE_BLK3_WDATA7_REG };
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
for (uint32_t r = block_start[i]; r <= block_end[i]; r+= 4) {
|
||||||
|
REG_WRITE(r, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void esp_efuse_disable_basic_rom_console(void)
|
||||||
|
{
|
||||||
|
if ((REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_CONSOLE_DEBUG_DISABLE) == 0) {
|
||||||
|
ESP_EARLY_LOGI(TAG, "Disable BASIC ROM Console fallback via efuse...");
|
||||||
|
esp_efuse_reset();
|
||||||
|
REG_WRITE(EFUSE_BLK0_WDATA6_REG, EFUSE_RD_CONSOLE_DEBUG_DISABLE);
|
||||||
|
esp_efuse_burn_new_values();
|
||||||
|
}
|
||||||
|
}
|
573
components/bootloader_support/src/esp_image_format.c
Normal file
573
components/bootloader_support/src/esp_image_format.c
Normal file
@ -0,0 +1,573 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
|
||||||
|
#include <rom/rtc.h>
|
||||||
|
#include <soc/cpu.h>
|
||||||
|
#include <esp_image_format.h>
|
||||||
|
#include <esp_secure_boot.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <esp_spi_flash.h>
|
||||||
|
#include <bootloader_flash.h>
|
||||||
|
#include <bootloader_random.h>
|
||||||
|
#include <bootloader_sha.h>
|
||||||
|
|
||||||
|
static const char *TAG = "esp_image";
|
||||||
|
|
||||||
|
#define HASH_LEN 32 /* SHA-256 digest length */
|
||||||
|
|
||||||
|
#define SIXTEEN_MB 0x1000000
|
||||||
|
#define ESP_ROM_CHECKSUM_INITIAL 0xEF
|
||||||
|
|
||||||
|
/* Headroom to ensure between stack SP (at time of checking) and data loaded from flash */
|
||||||
|
#define STACK_LOAD_HEADROOM 32768
|
||||||
|
|
||||||
|
/* Mmap source address mask */
|
||||||
|
#define MMAP_ALIGNED_MASK 0x0000FFFF
|
||||||
|
|
||||||
|
#ifdef BOOTLOADER_BUILD
|
||||||
|
/* 64 bits of random data to obfuscate loaded RAM with, until verification is complete
|
||||||
|
(Means loaded code isn't executable until after the secure boot check.)
|
||||||
|
*/
|
||||||
|
static uint32_t ram_obfs_value[2];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Return true if load_addr is an address the bootloader should load into */
|
||||||
|
static bool should_load(uint32_t load_addr);
|
||||||
|
/* Return true if load_addr is an address the bootloader should map via flash cache */
|
||||||
|
static bool should_map(uint32_t load_addr);
|
||||||
|
|
||||||
|
/* Load or verify a segment */
|
||||||
|
static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segment_header_t *header, bool silent, bool do_load, bootloader_sha256_handle_t sha_handle, uint32_t *checksum);
|
||||||
|
|
||||||
|
/* split segment and verify if data_len is too long */
|
||||||
|
static esp_err_t process_segment_data(intptr_t load_addr, uint32_t data_addr, uint32_t data_len, bool do_load, bootloader_sha256_handle_t sha_handle, uint32_t *checksum);
|
||||||
|
|
||||||
|
/* Verify the main image header */
|
||||||
|
static esp_err_t verify_image_header(uint32_t src_addr, const esp_image_header_t *image, bool silent);
|
||||||
|
|
||||||
|
/* Verify a segment header */
|
||||||
|
static esp_err_t verify_segment_header(int index, const esp_image_segment_header_t *segment, uint32_t segment_data_offs, bool silent);
|
||||||
|
|
||||||
|
/* Log-and-fail macro for use in esp_image_load */
|
||||||
|
#define FAIL_LOAD(...) do { \
|
||||||
|
if (!silent) { \
|
||||||
|
ESP_LOGE(TAG, __VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
goto err; \
|
||||||
|
} \
|
||||||
|
while(0)
|
||||||
|
|
||||||
|
static esp_err_t verify_checksum(bootloader_sha256_handle_t sha_handle, uint32_t checksum_word, esp_image_metadata_t *data);
|
||||||
|
|
||||||
|
static esp_err_t __attribute__((unused)) verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data);
|
||||||
|
static esp_err_t __attribute__((unused)) verify_simple_hash(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data);
|
||||||
|
|
||||||
|
esp_err_t esp_image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data)
|
||||||
|
{
|
||||||
|
#ifdef BOOTLOADER_BUILD
|
||||||
|
bool do_load = (mode == ESP_IMAGE_LOAD);
|
||||||
|
#else
|
||||||
|
bool do_load = false; // Can't load the image in app mode
|
||||||
|
#endif
|
||||||
|
bool silent = (mode == ESP_IMAGE_VERIFY_SILENT);
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
// checksum the image a word at a time. This shaves 30-40ms per MB of image size
|
||||||
|
uint32_t checksum_word = ESP_ROM_CHECKSUM_INITIAL;
|
||||||
|
bootloader_sha256_handle_t sha_handle = NULL;
|
||||||
|
|
||||||
|
if (data == NULL || part == NULL) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part->size > SIXTEEN_MB) {
|
||||||
|
err = ESP_ERR_INVALID_ARG;
|
||||||
|
FAIL_LOAD("partition size 0x%x invalid, larger than 16MB", part->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bzero(data, sizeof(esp_image_metadata_t));
|
||||||
|
data->start_addr = part->offset;
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "reading image header @ 0x%x", data->start_addr);
|
||||||
|
err = bootloader_flash_read(data->start_addr, &data->image, sizeof(esp_image_header_t), true);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate SHA-256 of image if secure boot is on, or if image has a hash appended
|
||||||
|
#ifdef CONFIG_SECURE_BOOT_ENABLED
|
||||||
|
if (1) {
|
||||||
|
#else
|
||||||
|
if (data->image.hash_appended) {
|
||||||
|
#endif
|
||||||
|
sha_handle = bootloader_sha256_start();
|
||||||
|
if (sha_handle == NULL) {
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
bootloader_sha256_data(sha_handle, &data->image, sizeof(esp_image_header_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "image header: 0x%02x 0x%02x 0x%02x 0x%02x %08x",
|
||||||
|
data->image.magic,
|
||||||
|
data->image.segment_count,
|
||||||
|
data->image.spi_mode,
|
||||||
|
data->image.spi_size,
|
||||||
|
data->image.entry_addr);
|
||||||
|
|
||||||
|
err = verify_image_header(data->start_addr, &data->image, silent);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->image.segment_count > ESP_IMAGE_MAX_SEGMENTS) {
|
||||||
|
FAIL_LOAD("image at 0x%x segment count %d exceeds max %d",
|
||||||
|
data->start_addr, data->image.segment_count, ESP_IMAGE_MAX_SEGMENTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t next_addr = data->start_addr + sizeof(esp_image_header_t);
|
||||||
|
for(int i = 0; i < data->image.segment_count; i++) {
|
||||||
|
esp_image_segment_header_t *header = &data->segments[i];
|
||||||
|
ESP_LOGV(TAG, "loading segment header %d at offset 0x%x", i, next_addr);
|
||||||
|
err = process_segment(i, next_addr, header, silent, do_load, sha_handle, &checksum_word);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
next_addr += sizeof(esp_image_segment_header_t);
|
||||||
|
data->segment_data[i] = next_addr;
|
||||||
|
next_addr += header->data_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Segments all loaded, verify length
|
||||||
|
uint32_t end_addr = next_addr;
|
||||||
|
if (end_addr < data->start_addr) {
|
||||||
|
FAIL_LOAD("image offset has wrapped");
|
||||||
|
}
|
||||||
|
|
||||||
|
data->image_len = end_addr - data->start_addr;
|
||||||
|
ESP_LOGV(TAG, "image start 0x%08x end of last section 0x%08x", data->start_addr, end_addr);
|
||||||
|
if (!esp_cpu_in_ocd_debug_mode()) {
|
||||||
|
err = verify_checksum(sha_handle, checksum_word, data);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data->image_len > part->size) {
|
||||||
|
FAIL_LOAD("Image length %d doesn't fit in partition length %d", data->image_len, part->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_bootloader = (data->start_addr == ESP_BOOTLOADER_OFFSET);
|
||||||
|
/* For secure boot, we don't verify signature on bootloaders.
|
||||||
|
|
||||||
|
For non-secure boot, we don't verify any SHA-256 hash appended to the bootloader because esptool.py may have
|
||||||
|
rewritten the header - rely on esptool.py having verified the bootloader at flashing time, instead.
|
||||||
|
*/
|
||||||
|
if (!is_bootloader) {
|
||||||
|
#ifdef CONFIG_SECURE_BOOT_ENABLED
|
||||||
|
// secure boot images have a signature appended
|
||||||
|
err = verify_secure_boot_signature(sha_handle, data);
|
||||||
|
#else
|
||||||
|
// No secure boot, but SHA-256 can be appended for basic corruption detection
|
||||||
|
if (sha_handle != NULL && !esp_cpu_in_ocd_debug_mode()) {
|
||||||
|
err = verify_simple_hash(sha_handle, data);
|
||||||
|
}
|
||||||
|
#endif // CONFIG_SECURE_BOOT_ENABLED
|
||||||
|
} else { // is_bootloader
|
||||||
|
// bootloader may still have a sha256 digest handle open
|
||||||
|
if (sha_handle != NULL) {
|
||||||
|
bootloader_sha256_finish(sha_handle, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sha_handle = NULL;
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BOOTLOADER_BUILD
|
||||||
|
if (do_load) { // Need to deobfuscate RAM
|
||||||
|
for (int i = 0; i < data->image.segment_count; i++) {
|
||||||
|
uint32_t load_addr = data->segments[i].load_addr;
|
||||||
|
if (should_load(load_addr)) {
|
||||||
|
uint32_t *loaded = (uint32_t *)load_addr;
|
||||||
|
for (int j = 0; j < data->segments[i].data_len/sizeof(uint32_t); j++) {
|
||||||
|
loaded[j] ^= (j & 1) ? ram_obfs_value[0] : ram_obfs_value[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Success!
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
err = ESP_ERR_IMAGE_INVALID;
|
||||||
|
}
|
||||||
|
if (sha_handle != NULL) {
|
||||||
|
// Need to finish the hash process to free the handle
|
||||||
|
bootloader_sha256_finish(sha_handle, NULL);
|
||||||
|
}
|
||||||
|
// Prevent invalid/incomplete data leaking out
|
||||||
|
bzero(data, sizeof(esp_image_metadata_t));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t verify_image_header(uint32_t src_addr, const esp_image_header_t *image, bool silent)
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
|
||||||
|
if (image->magic != ESP_IMAGE_HEADER_MAGIC) {
|
||||||
|
if (!silent) {
|
||||||
|
ESP_LOGE(TAG, "image at 0x%x has invalid magic byte", src_addr);
|
||||||
|
}
|
||||||
|
err = ESP_ERR_IMAGE_INVALID;
|
||||||
|
}
|
||||||
|
if (!silent) {
|
||||||
|
if (image->spi_mode > ESP_IMAGE_SPI_MODE_SLOW_READ) {
|
||||||
|
ESP_LOGW(TAG, "image at 0x%x has invalid SPI mode %d", src_addr, image->spi_mode);
|
||||||
|
}
|
||||||
|
if (image->spi_speed > ESP_IMAGE_SPI_SPEED_80M) {
|
||||||
|
ESP_LOGW(TAG, "image at 0x%x has invalid SPI speed %d", src_addr, image->spi_speed);
|
||||||
|
}
|
||||||
|
if (image->spi_size > ESP_IMAGE_FLASH_SIZE_MAX) {
|
||||||
|
ESP_LOGW(TAG, "image at 0x%x has invalid SPI size %d", src_addr, image->spi_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segment_header_t *header, bool silent, bool do_load, bootloader_sha256_handle_t sha_handle, uint32_t *checksum)
|
||||||
|
{
|
||||||
|
esp_err_t err;
|
||||||
|
|
||||||
|
/* read segment header */
|
||||||
|
err = bootloader_flash_read(flash_addr, header, sizeof(esp_image_segment_header_t), true);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "bootloader_flash_read failed at 0x%08x", flash_addr);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (sha_handle != NULL) {
|
||||||
|
bootloader_sha256_data(sha_handle, header, sizeof(esp_image_segment_header_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
intptr_t load_addr = header->load_addr;
|
||||||
|
uint32_t data_len = header->data_len;
|
||||||
|
uint32_t data_addr = flash_addr + sizeof(esp_image_segment_header_t);
|
||||||
|
|
||||||
|
ESP_LOGV(TAG, "segment data length 0x%x data starts 0x%x", data_len, data_addr);
|
||||||
|
|
||||||
|
err = verify_segment_header(index, header, data_addr, silent);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_len % 4 != 0) {
|
||||||
|
FAIL_LOAD("unaligned segment length 0x%x", data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_mapping = should_map(load_addr);
|
||||||
|
do_load = do_load && should_load(load_addr);
|
||||||
|
|
||||||
|
if (!silent) {
|
||||||
|
ESP_LOGI(TAG, "segment %d: paddr=0x%08x vaddr=0x%08x size=0x%05x (%6d) %s",
|
||||||
|
index, data_addr, load_addr,
|
||||||
|
data_len, data_len,
|
||||||
|
(do_load)?"load":(is_mapping)?"map":"");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (do_load) {
|
||||||
|
/* Before loading segment, check it doesn't clobber bootloader RAM... */
|
||||||
|
uint32_t end_addr = load_addr + data_len;
|
||||||
|
if (end_addr < 0x40000000) {
|
||||||
|
intptr_t sp = (intptr_t)get_sp();
|
||||||
|
if (end_addr > sp - STACK_LOAD_HEADROOM) {
|
||||||
|
ESP_LOGE(TAG, "Segment %d end address 0x%08x too high (bootloader stack 0x%08x liimit 0x%08x)",
|
||||||
|
index, end_addr, sp, sp - STACK_LOAD_HEADROOM);
|
||||||
|
return ESP_ERR_IMAGE_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifndef BOOTLOADER_BUILD
|
||||||
|
uint32_t free_page_count = spi_flash_mmap_get_free_pages(SPI_FLASH_MMAP_DATA);
|
||||||
|
ESP_LOGD(TAG, "free data page_count 0x%08x",free_page_count);
|
||||||
|
uint32_t offset_page = 0;
|
||||||
|
while (data_len >= free_page_count * SPI_FLASH_MMU_PAGE_SIZE) {
|
||||||
|
offset_page = ((data_addr & MMAP_ALIGNED_MASK) != 0)?1:0;
|
||||||
|
err = process_segment_data(load_addr, data_addr, (free_page_count - offset_page) * SPI_FLASH_MMU_PAGE_SIZE, do_load, sha_handle, checksum);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
data_addr += (free_page_count - offset_page) * SPI_FLASH_MMU_PAGE_SIZE;
|
||||||
|
data_len -= (free_page_count - offset_page) * SPI_FLASH_MMU_PAGE_SIZE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
err = process_segment_data(load_addr, data_addr, data_len, do_load, sha_handle, checksum);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
err = ESP_ERR_IMAGE_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t process_segment_data(intptr_t load_addr, uint32_t data_addr, uint32_t data_len, bool do_load, bootloader_sha256_handle_t sha_handle, uint32_t *checksum)
|
||||||
|
{
|
||||||
|
const uint32_t *data = (const uint32_t *)bootloader_mmap(data_addr, data_len);
|
||||||
|
if(!data) {
|
||||||
|
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed",
|
||||||
|
data_addr, data_len);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BOOTLOADER_BUILD
|
||||||
|
// Set up the obfuscation value to use for loading
|
||||||
|
while (ram_obfs_value[0] == 0 || ram_obfs_value[1] == 0) {
|
||||||
|
bootloader_fill_random(ram_obfs_value, sizeof(ram_obfs_value));
|
||||||
|
}
|
||||||
|
uint32_t *dest = (uint32_t *)load_addr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const uint32_t *src = data;
|
||||||
|
|
||||||
|
for (int i = 0; i < data_len; i += 4) {
|
||||||
|
int w_i = i/4; // Word index
|
||||||
|
uint32_t w = src[w_i];
|
||||||
|
*checksum ^= w;
|
||||||
|
#ifdef BOOTLOADER_BUILD
|
||||||
|
if (do_load) {
|
||||||
|
dest[w_i] = w ^ ((w_i & 1) ? ram_obfs_value[0] : ram_obfs_value[1]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// SHA_CHUNK determined experimentally as the optimum size
|
||||||
|
// to call bootloader_sha256_data() with. This is a bit
|
||||||
|
// counter-intuitive, but it's ~3ms better than using the
|
||||||
|
// SHA256 block size.
|
||||||
|
const size_t SHA_CHUNK = 1024;
|
||||||
|
if (sha_handle != NULL && i % SHA_CHUNK == 0) {
|
||||||
|
bootloader_sha256_data(sha_handle, &src[w_i],
|
||||||
|
MIN(SHA_CHUNK, data_len - i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bootloader_munmap(data);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t verify_segment_header(int index, const esp_image_segment_header_t *segment, uint32_t segment_data_offs, bool silent)
|
||||||
|
{
|
||||||
|
if ((segment->data_len & 3) != 0
|
||||||
|
|| segment->data_len >= SIXTEEN_MB) {
|
||||||
|
if (!silent) {
|
||||||
|
ESP_LOGE(TAG, "invalid segment length 0x%x", segment->data_len);
|
||||||
|
}
|
||||||
|
return ESP_ERR_IMAGE_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t load_addr = segment->load_addr;
|
||||||
|
bool map_segment = should_map(load_addr);
|
||||||
|
|
||||||
|
/* Check that flash cache mapped segment aligns correctly from flash to its mapped address,
|
||||||
|
relative to the 64KB page mapping size.
|
||||||
|
*/
|
||||||
|
ESP_LOGV(TAG, "segment %d map_segment %d segment_data_offs 0x%x load_addr 0x%x",
|
||||||
|
index, map_segment, segment_data_offs, load_addr);
|
||||||
|
if (map_segment
|
||||||
|
&& ((segment_data_offs % SPI_FLASH_MMU_PAGE_SIZE) != (load_addr % SPI_FLASH_MMU_PAGE_SIZE))) {
|
||||||
|
if (!silent) {
|
||||||
|
ESP_LOGE(TAG, "Segment %d load address 0x%08x, doesn't match data 0x%08x",
|
||||||
|
index, load_addr, segment_data_offs);
|
||||||
|
}
|
||||||
|
return ESP_ERR_IMAGE_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool should_map(uint32_t load_addr)
|
||||||
|
{
|
||||||
|
return (load_addr >= SOC_IROM_LOW && load_addr < SOC_IROM_HIGH)
|
||||||
|
|| (load_addr >= SOC_DROM_LOW && load_addr < SOC_DROM_HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool should_load(uint32_t load_addr)
|
||||||
|
{
|
||||||
|
/* Reload the RTC memory segments whenever a non-deepsleep reset
|
||||||
|
is occurring */
|
||||||
|
bool load_rtc_memory = rtc_get_reset_reason(0) != DEEPSLEEP_RESET;
|
||||||
|
|
||||||
|
if (should_map(load_addr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (load_addr < 0x10000000) {
|
||||||
|
// Reserved for non-loaded addresses.
|
||||||
|
// Current reserved values are
|
||||||
|
// 0x0 (padding block)
|
||||||
|
// 0x4 (unused, but reserved for an MD5 block)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!load_rtc_memory) {
|
||||||
|
if (load_addr >= SOC_RTC_IRAM_LOW && load_addr < SOC_RTC_IRAM_HIGH) {
|
||||||
|
ESP_LOGD(TAG, "Skipping RTC code segment at 0x%08x\n", load_addr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (load_addr >= SOC_RTC_DATA_LOW && load_addr < SOC_RTC_DATA_HIGH) {
|
||||||
|
ESP_LOGD(TAG, "Skipping RTC data segment at 0x%08x\n", load_addr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_image_verify_bootloader(uint32_t *length)
|
||||||
|
{
|
||||||
|
esp_image_metadata_t data;
|
||||||
|
const esp_partition_pos_t bootloader_part = {
|
||||||
|
.offset = ESP_BOOTLOADER_OFFSET,
|
||||||
|
.size = ESP_PARTITION_TABLE_OFFSET - ESP_BOOTLOADER_OFFSET,
|
||||||
|
};
|
||||||
|
esp_err_t err = esp_image_load(ESP_IMAGE_VERIFY,
|
||||||
|
&bootloader_part,
|
||||||
|
&data);
|
||||||
|
if (length != NULL) {
|
||||||
|
*length = (err == ESP_OK) ? data.image_len : 0;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t verify_checksum(bootloader_sha256_handle_t sha_handle, uint32_t checksum_word, esp_image_metadata_t *data)
|
||||||
|
{
|
||||||
|
uint32_t unpadded_length = data->image_len;
|
||||||
|
uint32_t length = unpadded_length + 1; // Add a byte for the checksum
|
||||||
|
length = (length + 15) & ~15; // Pad to next full 16 byte block
|
||||||
|
|
||||||
|
// Verify checksum
|
||||||
|
uint8_t buf[16];
|
||||||
|
esp_err_t err = bootloader_flash_read(data->start_addr + unpadded_length, buf, length - unpadded_length, true);
|
||||||
|
uint8_t calc = buf[length - unpadded_length - 1];
|
||||||
|
uint8_t checksum = (checksum_word >> 24)
|
||||||
|
^ (checksum_word >> 16)
|
||||||
|
^ (checksum_word >> 8)
|
||||||
|
^ (checksum_word >> 0);
|
||||||
|
if (err != ESP_OK || checksum != calc) {
|
||||||
|
ESP_LOGE(TAG, "Checksum failed. Calculated 0x%x read 0x%x", checksum, calc);
|
||||||
|
return ESP_ERR_IMAGE_INVALID;
|
||||||
|
}
|
||||||
|
if (sha_handle != NULL) {
|
||||||
|
bootloader_sha256_data(sha_handle, buf, length - unpadded_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->image.hash_appended) {
|
||||||
|
// Account for the hash in the total image length
|
||||||
|
length += HASH_LEN;
|
||||||
|
}
|
||||||
|
data->image_len = length;
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void debug_log_hash(const uint8_t *image_hash, const char *caption);
|
||||||
|
|
||||||
|
static esp_err_t verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data)
|
||||||
|
{
|
||||||
|
uint8_t image_hash[HASH_LEN] = { 0 };
|
||||||
|
|
||||||
|
// For secure boot, we calculate the signature hash over the whole file, which includes any "simple" hash
|
||||||
|
// appended to the image for corruption detection
|
||||||
|
if (data->image.hash_appended) {
|
||||||
|
const void *simple_hash = bootloader_mmap(data->start_addr + data->image_len - HASH_LEN, HASH_LEN);
|
||||||
|
bootloader_sha256_data(sha_handle, simple_hash, HASH_LEN);
|
||||||
|
bootloader_munmap(simple_hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
bootloader_sha256_finish(sha_handle, image_hash);
|
||||||
|
|
||||||
|
// Log the hash for debugging
|
||||||
|
debug_log_hash(image_hash, "Calculated secure boot hash");
|
||||||
|
|
||||||
|
// Use hash to verify signature block
|
||||||
|
const esp_secure_boot_sig_block_t *sig_block = bootloader_mmap(data->start_addr + data->image_len, sizeof(esp_secure_boot_sig_block_t));
|
||||||
|
esp_err_t err = esp_secure_boot_verify_signature_block(sig_block, image_hash);
|
||||||
|
bootloader_munmap(sig_block);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Secure boot signature verification failed");
|
||||||
|
|
||||||
|
// Go back and check if the simple hash matches or not (we're off the fast path so we can re-hash the whole image now)
|
||||||
|
ESP_LOGI(TAG, "Calculating simple hash to check for corruption...");
|
||||||
|
const void *whole_image = bootloader_mmap(data->start_addr, data->image_len - HASH_LEN);
|
||||||
|
if (whole_image != NULL) {
|
||||||
|
sha_handle = bootloader_sha256_start();
|
||||||
|
bootloader_sha256_data(sha_handle, whole_image, data->image_len - HASH_LEN);
|
||||||
|
bootloader_munmap(whole_image);
|
||||||
|
if (verify_simple_hash(sha_handle, data) != ESP_OK) {
|
||||||
|
ESP_LOGW(TAG, "image corrupted on flash");
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "image valid, signature bad");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_ERR_IMAGE_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t verify_simple_hash(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data)
|
||||||
|
{
|
||||||
|
uint8_t image_hash[HASH_LEN] = { 0 };
|
||||||
|
bootloader_sha256_finish(sha_handle, image_hash);
|
||||||
|
|
||||||
|
// Log the hash for debugging
|
||||||
|
debug_log_hash(image_hash, "Calculated hash");
|
||||||
|
|
||||||
|
// Simple hash for verification only
|
||||||
|
const void *hash = bootloader_mmap(data->start_addr + data->image_len - HASH_LEN, HASH_LEN);
|
||||||
|
if (memcmp(hash, image_hash, HASH_LEN) != 0) {
|
||||||
|
ESP_LOGE(TAG, "Image hash failed - image is corrupt");
|
||||||
|
debug_log_hash(hash, "Expected hash");
|
||||||
|
bootloader_munmap(hash);
|
||||||
|
return ESP_ERR_IMAGE_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
bootloader_munmap(hash);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log a hash as a hex string
|
||||||
|
static void debug_log_hash(const uint8_t *image_hash, const char *label)
|
||||||
|
{
|
||||||
|
#if BOOT_LOG_LEVEL >= LOG_LEVEL_DEBUG
|
||||||
|
char hash_print[sizeof(image_hash)*2 + 1];
|
||||||
|
hash_print[sizeof(image_hash)*2] = 0;
|
||||||
|
for (int i = 0; i < sizeof(image_hash); i++) {
|
||||||
|
for (int shift = 0; shift < 2; shift++) {
|
||||||
|
uint8_t nibble = (image_hash[i] >> (shift ? 0 : 4)) & 0x0F;
|
||||||
|
if (nibble < 10) {
|
||||||
|
hash_print[i*2+shift] = '0' + nibble;
|
||||||
|
} else {
|
||||||
|
hash_print[i*2+shift] = 'a' + nibble - 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "%s: %s", label, hash_print);
|
||||||
|
#endif
|
||||||
|
}
|
339
components/bootloader_support/src/flash_encrypt.c
Normal file
339
components/bootloader_support/src/flash_encrypt.c
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <strings.h>
|
||||||
|
|
||||||
|
#include "bootloader_flash.h"
|
||||||
|
#include "bootloader_random.h"
|
||||||
|
#include "esp_image_format.h"
|
||||||
|
#include "esp_flash_encrypt.h"
|
||||||
|
#include "esp_flash_partitions.h"
|
||||||
|
#include "esp_flash_data_types.h"
|
||||||
|
#include "esp_secure_boot.h"
|
||||||
|
#include "esp_efuse.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "rom/secure_boot.h"
|
||||||
|
|
||||||
|
#include "rom/cache.h"
|
||||||
|
#include "rom/spi_flash.h" /* TODO: Remove this */
|
||||||
|
|
||||||
|
static const char *TAG = "flash_encrypt";
|
||||||
|
|
||||||
|
/* Static functions for stages of flash encryption */
|
||||||
|
static esp_err_t initialise_flash_encryption(void);
|
||||||
|
static esp_err_t encrypt_flash_contents(uint32_t flash_crypt_cnt, bool flash_crypt_wr_dis);
|
||||||
|
static esp_err_t encrypt_bootloader();
|
||||||
|
static esp_err_t encrypt_and_load_partition_table(esp_partition_info_t *partition_table, int *num_partitions);
|
||||||
|
static esp_err_t encrypt_partition(int index, const esp_partition_info_t *partition);
|
||||||
|
|
||||||
|
esp_err_t esp_flash_encrypt_check_and_update(void)
|
||||||
|
{
|
||||||
|
uint32_t efuse_blk0 = REG_READ(EFUSE_BLK0_RDATA0_REG);
|
||||||
|
ESP_LOGV(TAG, "efuse_blk0 raw value %08x", efuse_blk0);
|
||||||
|
uint32_t flash_crypt_cnt = (efuse_blk0 & EFUSE_RD_FLASH_CRYPT_CNT_M) >> EFUSE_RD_FLASH_CRYPT_CNT_S;
|
||||||
|
bool flash_crypt_wr_dis = efuse_blk0 & EFUSE_WR_DIS_FLASH_CRYPT_CNT;
|
||||||
|
ESP_LOGV(TAG, "efuse FLASH_CRYPT_CNT 0x%x WR_DIS_FLASH_CRYPT_CNT 0x%x", flash_crypt_cnt, flash_crypt_wr_dis);
|
||||||
|
|
||||||
|
if (__builtin_parity(flash_crypt_cnt) == 1) {
|
||||||
|
/* Flash is already encrypted */
|
||||||
|
int left = (7 - __builtin_popcount(flash_crypt_cnt)) / 2;
|
||||||
|
if (flash_crypt_wr_dis) {
|
||||||
|
left = 0; /* can't update FLASH_CRYPT_CNT, no more flashes */
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "flash encryption is enabled (%d plaintext flashes left)", left);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Flash is not encrypted, so encrypt it! */
|
||||||
|
return encrypt_flash_contents(flash_crypt_cnt, flash_crypt_wr_dis);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t initialise_flash_encryption(void)
|
||||||
|
{
|
||||||
|
/* Before first flash encryption pass, need to initialise key & crypto config */
|
||||||
|
|
||||||
|
/* Generate key */
|
||||||
|
uint32_t dis_reg = REG_READ(EFUSE_BLK0_RDATA0_REG);
|
||||||
|
bool efuse_key_read_protected = dis_reg & EFUSE_RD_DIS_BLK1;
|
||||||
|
bool efuse_key_write_protected = dis_reg & EFUSE_WR_DIS_BLK1;
|
||||||
|
if (efuse_key_read_protected == false
|
||||||
|
&& efuse_key_write_protected == false
|
||||||
|
&& REG_READ(EFUSE_BLK1_RDATA0_REG) == 0
|
||||||
|
&& REG_READ(EFUSE_BLK1_RDATA1_REG) == 0
|
||||||
|
&& REG_READ(EFUSE_BLK1_RDATA2_REG) == 0
|
||||||
|
&& REG_READ(EFUSE_BLK1_RDATA3_REG) == 0
|
||||||
|
&& REG_READ(EFUSE_BLK1_RDATA4_REG) == 0
|
||||||
|
&& REG_READ(EFUSE_BLK1_RDATA5_REG) == 0
|
||||||
|
&& REG_READ(EFUSE_BLK1_RDATA6_REG) == 0
|
||||||
|
&& REG_READ(EFUSE_BLK1_RDATA7_REG) == 0) {
|
||||||
|
ESP_LOGI(TAG, "Generating new flash encryption key...");
|
||||||
|
uint32_t buf[8];
|
||||||
|
bootloader_fill_random(buf, sizeof(buf));
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
ESP_LOGV(TAG, "EFUSE_BLK1_WDATA%d_REG = 0x%08x", i, buf[i]);
|
||||||
|
REG_WRITE(EFUSE_BLK1_WDATA0_REG + 4*i, buf[i]);
|
||||||
|
}
|
||||||
|
bzero(buf, sizeof(buf));
|
||||||
|
esp_efuse_burn_new_values();
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Read & write protecting new key...");
|
||||||
|
REG_WRITE(EFUSE_BLK0_WDATA0_REG, EFUSE_WR_DIS_BLK1 | EFUSE_RD_DIS_BLK1);
|
||||||
|
esp_efuse_burn_new_values();
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if(!(efuse_key_read_protected && efuse_key_write_protected)) {
|
||||||
|
ESP_LOGE(TAG, "Flash encryption key has to be either unset or both read and write protected");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
ESP_LOGW(TAG, "Using pre-loaded flash encryption key in EFUSE block 1");
|
||||||
|
}
|
||||||
|
/* CRYPT_CONFIG determines which bits of the AES block key are XORed
|
||||||
|
with bits from the flash address, to provide the key tweak.
|
||||||
|
|
||||||
|
CRYPT_CONFIG == 0 is effectively AES ECB mode (NOT SUPPORTED)
|
||||||
|
|
||||||
|
For now this is hardcoded to XOR all 256 bits of the key.
|
||||||
|
|
||||||
|
If you need to override it, you can pre-burn this efuse to the
|
||||||
|
desired value and then write-protect it, in which case this
|
||||||
|
operation does nothing. Please note this is not recommended!
|
||||||
|
*/
|
||||||
|
ESP_LOGI(TAG, "Setting CRYPT_CONFIG efuse to 0xF");
|
||||||
|
REG_WRITE(EFUSE_BLK0_WDATA5_REG, EFUSE_FLASH_CRYPT_CONFIG_M);
|
||||||
|
esp_efuse_burn_new_values();
|
||||||
|
|
||||||
|
uint32_t new_wdata6 = 0;
|
||||||
|
#ifndef CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_ENCRYPT
|
||||||
|
ESP_LOGI(TAG, "Disable UART bootloader encryption...");
|
||||||
|
new_wdata6 |= EFUSE_DISABLE_DL_ENCRYPT;
|
||||||
|
#else
|
||||||
|
ESP_LOGW(TAG, "Not disabling UART bootloader encryption");
|
||||||
|
#endif
|
||||||
|
#ifndef CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_DECRYPT
|
||||||
|
ESP_LOGI(TAG, "Disable UART bootloader decryption...");
|
||||||
|
new_wdata6 |= EFUSE_DISABLE_DL_DECRYPT;
|
||||||
|
#else
|
||||||
|
ESP_LOGW(TAG, "Not disabling UART bootloader decryption - SECURITY COMPROMISED");
|
||||||
|
#endif
|
||||||
|
#ifndef CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_CACHE
|
||||||
|
ESP_LOGI(TAG, "Disable UART bootloader MMU cache...");
|
||||||
|
new_wdata6 |= EFUSE_DISABLE_DL_CACHE;
|
||||||
|
#else
|
||||||
|
ESP_LOGW(TAG, "Not disabling UART bootloader MMU cache - SECURITY COMPROMISED");
|
||||||
|
#endif
|
||||||
|
#ifndef CONFIG_SECURE_BOOT_ALLOW_JTAG
|
||||||
|
ESP_LOGI(TAG, "Disable JTAG...");
|
||||||
|
new_wdata6 |= EFUSE_RD_DISABLE_JTAG;
|
||||||
|
#else
|
||||||
|
ESP_LOGW(TAG, "Not disabling JTAG - SECURITY COMPROMISED");
|
||||||
|
#endif
|
||||||
|
#ifndef CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC
|
||||||
|
ESP_LOGI(TAG, "Disable ROM BASIC interpreter fallback...");
|
||||||
|
new_wdata6 |= EFUSE_RD_CONSOLE_DEBUG_DISABLE;
|
||||||
|
#else
|
||||||
|
ESP_LOGW(TAG, "Not disabling ROM BASIC fallback - SECURITY COMPROMISED");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (new_wdata6 != 0) {
|
||||||
|
REG_WRITE(EFUSE_BLK0_WDATA6_REG, new_wdata6);
|
||||||
|
esp_efuse_burn_new_values();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encrypt all flash data that should be encrypted */
|
||||||
|
static esp_err_t encrypt_flash_contents(uint32_t flash_crypt_cnt, bool flash_crypt_wr_dis)
|
||||||
|
{
|
||||||
|
esp_err_t err;
|
||||||
|
esp_partition_info_t partition_table[ESP_PARTITION_TABLE_MAX_ENTRIES];
|
||||||
|
int num_partitions;
|
||||||
|
|
||||||
|
/* If the last flash_crypt_cnt bit is burned or write-disabled, the
|
||||||
|
device can't re-encrypt itself. */
|
||||||
|
if (flash_crypt_wr_dis || flash_crypt_cnt == 0xFF) {
|
||||||
|
ESP_LOGE(TAG, "Cannot re-encrypt data (FLASH_CRYPT_CNT 0x%02x write disabled %d", flash_crypt_cnt, flash_crypt_wr_dis);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flash_crypt_cnt == 0) {
|
||||||
|
/* Very first flash of encrypted data: generate keys, etc. */
|
||||||
|
err = initialise_flash_encryption();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = encrypt_bootloader();
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = encrypt_and_load_partition_table(partition_table, &num_partitions);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now iterate the just-loaded partition table, looking for entries to encrypt
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Go through each partition and encrypt if necessary */
|
||||||
|
for (int i = 0; i < num_partitions; i++) {
|
||||||
|
err = encrypt_partition(i, &partition_table[i]);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "All flash regions checked for encryption pass");
|
||||||
|
|
||||||
|
/* Set least significant 0-bit in flash_crypt_cnt */
|
||||||
|
int ffs_inv = __builtin_ffs((~flash_crypt_cnt) & 0xFF);
|
||||||
|
/* ffs_inv shouldn't be zero, as zero implies flash_crypt_cnt == 0xFF */
|
||||||
|
uint32_t new_flash_crypt_cnt = flash_crypt_cnt + (1 << (ffs_inv - 1));
|
||||||
|
ESP_LOGD(TAG, "FLASH_CRYPT_CNT 0x%x -> 0x%x", flash_crypt_cnt, new_flash_crypt_cnt);
|
||||||
|
REG_SET_FIELD(EFUSE_BLK0_WDATA0_REG, EFUSE_FLASH_CRYPT_CNT, new_flash_crypt_cnt);
|
||||||
|
esp_efuse_burn_new_values();
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Flash encryption completed");
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t encrypt_bootloader()
|
||||||
|
{
|
||||||
|
esp_err_t err;
|
||||||
|
uint32_t image_length;
|
||||||
|
/* Check for plaintext bootloader (verification will fail if it's already encrypted) */
|
||||||
|
if (esp_image_verify_bootloader(&image_length) == ESP_OK) {
|
||||||
|
ESP_LOGD(TAG, "bootloader is plaintext. Encrypting...");
|
||||||
|
err = esp_flash_encrypt_region(ESP_BOOTLOADER_OFFSET, image_length);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to encrypt bootloader in place: 0x%x", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esp_secure_boot_enabled()) {
|
||||||
|
/* If secure boot is enabled and bootloader was plaintext, also
|
||||||
|
need to encrypt secure boot IV+digest.
|
||||||
|
*/
|
||||||
|
ESP_LOGD(TAG, "Encrypting secure bootloader IV & digest...");
|
||||||
|
err = esp_flash_encrypt_region(FLASH_OFFS_SECURE_BOOT_IV_DIGEST,
|
||||||
|
FLASH_SECTOR_SIZE);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to encrypt bootloader IV & digest in place: 0x%x", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ESP_LOGW(TAG, "no valid bootloader was found");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t encrypt_and_load_partition_table(esp_partition_info_t *partition_table, int *num_partitions)
|
||||||
|
{
|
||||||
|
esp_err_t err;
|
||||||
|
/* Check for plaintext partition table */
|
||||||
|
err = bootloader_flash_read(ESP_PARTITION_TABLE_OFFSET, partition_table, ESP_PARTITION_TABLE_MAX_LEN, false);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to read partition table data");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (esp_partition_table_basic_verify(partition_table, false, num_partitions) == ESP_OK) {
|
||||||
|
ESP_LOGD(TAG, "partition table is plaintext. Encrypting...");
|
||||||
|
esp_err_t err = esp_flash_encrypt_region(ESP_PARTITION_TABLE_OFFSET,
|
||||||
|
FLASH_SECTOR_SIZE);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to encrypt partition table in place. %x", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ESP_LOGE(TAG, "Failed to read partition table data - not plaintext?");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Valid partition table loded */
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static esp_err_t encrypt_partition(int index, const esp_partition_info_t *partition)
|
||||||
|
{
|
||||||
|
esp_err_t err;
|
||||||
|
bool should_encrypt = (partition->flags & PART_FLAG_ENCRYPTED);
|
||||||
|
|
||||||
|
if (partition->type == PART_TYPE_APP) {
|
||||||
|
/* check if the partition holds a valid unencrypted app */
|
||||||
|
esp_image_metadata_t data_ignored;
|
||||||
|
err = esp_image_load(ESP_IMAGE_VERIFY,
|
||||||
|
&partition->pos,
|
||||||
|
&data_ignored);
|
||||||
|
should_encrypt = (err == ESP_OK);
|
||||||
|
} else if (partition->type == PART_TYPE_DATA && partition->subtype == PART_SUBTYPE_DATA_OTA) {
|
||||||
|
/* check if we have ota data partition and the partition should be encrypted unconditionally */
|
||||||
|
should_encrypt = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!should_encrypt) {
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* should_encrypt */
|
||||||
|
ESP_LOGI(TAG, "Encrypting partition %d at offset 0x%x...", index, partition->pos.offset);
|
||||||
|
|
||||||
|
err = esp_flash_encrypt_region(partition->pos.offset, partition->pos.size);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to encrypt partition %d", index);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
esp_err_t esp_flash_encrypt_region(uint32_t src_addr, size_t data_length)
|
||||||
|
{
|
||||||
|
esp_err_t err;
|
||||||
|
uint32_t buf[FLASH_SECTOR_SIZE / sizeof(uint32_t)];
|
||||||
|
|
||||||
|
if (src_addr % FLASH_SECTOR_SIZE != 0) {
|
||||||
|
ESP_LOGE(TAG, "esp_flash_encrypt_region bad src_addr 0x%x",src_addr);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < data_length; i += FLASH_SECTOR_SIZE) {
|
||||||
|
uint32_t sec_start = i + src_addr;
|
||||||
|
err = bootloader_flash_read(sec_start, buf, FLASH_SECTOR_SIZE, false);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
goto flash_failed;
|
||||||
|
}
|
||||||
|
err = bootloader_flash_erase_sector(sec_start / FLASH_SECTOR_SIZE);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
goto flash_failed;
|
||||||
|
}
|
||||||
|
err = bootloader_flash_write(sec_start, buf, FLASH_SECTOR_SIZE, true);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
goto flash_failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
|
||||||
|
flash_failed:
|
||||||
|
ESP_LOGE(TAG, "flash operation failed: 0x%x", err);
|
||||||
|
return err;
|
||||||
|
}
|
86
components/bootloader_support/src/flash_partitions.c
Normal file
86
components/bootloader_support/src/flash_partitions.c
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <string.h>
|
||||||
|
#include "esp_flash_partitions.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "rom/spi_flash.h"
|
||||||
|
#include "rom/md5_hash.h"
|
||||||
|
#include "esp_flash_data_types.h"
|
||||||
|
|
||||||
|
static const char *TAG = "flash_parts";
|
||||||
|
|
||||||
|
esp_err_t esp_partition_table_basic_verify(const esp_partition_info_t *partition_table, bool log_errors, int *num_partitions)
|
||||||
|
{
|
||||||
|
int md5_found = 0;
|
||||||
|
int num_parts;
|
||||||
|
uint32_t chip_size = g_rom_flashchip.chip_size;
|
||||||
|
*num_partitions = 0;
|
||||||
|
|
||||||
|
for (num_parts = 0; num_parts < ESP_PARTITION_TABLE_MAX_ENTRIES; num_parts++) {
|
||||||
|
const esp_partition_info_t *part = &partition_table[num_parts];
|
||||||
|
|
||||||
|
if (part->magic == ESP_PARTITION_MAGIC) {
|
||||||
|
const esp_partition_pos_t *pos = &part->pos;
|
||||||
|
if (pos->offset > chip_size || pos->offset + pos->size > chip_size) {
|
||||||
|
if (log_errors) {
|
||||||
|
ESP_LOGE(TAG, "partition %d invalid - offset 0x%x size 0x%x exceeds flash chip size 0x%x",
|
||||||
|
num_parts, pos->offset, pos->size, chip_size);
|
||||||
|
}
|
||||||
|
return ESP_ERR_INVALID_SIZE;
|
||||||
|
}
|
||||||
|
} else if (part->magic == ESP_PARTITION_MAGIC_MD5) {
|
||||||
|
if (md5_found) {
|
||||||
|
if (log_errors) {
|
||||||
|
ESP_LOGE(TAG, "Only one MD5 checksum is allowed");
|
||||||
|
}
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MD5Context context;
|
||||||
|
unsigned char digest[16];
|
||||||
|
MD5Init(&context);
|
||||||
|
MD5Update(&context, (unsigned char *) partition_table, num_parts * sizeof(esp_partition_info_t));
|
||||||
|
MD5Final(digest, &context);
|
||||||
|
|
||||||
|
unsigned char *md5sum = ((unsigned char *) part) + 16; // skip the 2B magic number and the 14B fillup bytes
|
||||||
|
|
||||||
|
if (memcmp(md5sum, digest, sizeof(digest)) != 0) {
|
||||||
|
if (log_errors) {
|
||||||
|
ESP_LOGE(TAG, "Incorrect MD5 checksum");
|
||||||
|
}
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
//MD5 checksum matches and we continue with the next interation in
|
||||||
|
//order to detect the end of the partition table
|
||||||
|
md5_found = 1;
|
||||||
|
} else if (part->magic == 0xFFFF
|
||||||
|
&& part->type == PART_TYPE_END
|
||||||
|
&& part->subtype == PART_SUBTYPE_END) {
|
||||||
|
ESP_LOGD(TAG, "partition table verified, %d entries", num_parts);
|
||||||
|
*num_partitions = num_parts - md5_found; //do not count the partition where the MD5 checksum is held
|
||||||
|
return ESP_OK;
|
||||||
|
} else {
|
||||||
|
if (log_errors) {
|
||||||
|
ESP_LOGE(TAG, "partition %d invalid magic number 0x%x", num_parts, part->magic);
|
||||||
|
}
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log_errors) {
|
||||||
|
ESP_LOGE(TAG, "partition table has no terminating entry, not valid");
|
||||||
|
}
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
282
components/bootloader_support/src/flash_qio_mode.c
Normal file
282
components/bootloader_support/src/flash_qio_mode.c
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "flash_qio_mode.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "rom/spi_flash.h"
|
||||||
|
#include "rom/efuse.h"
|
||||||
|
#include "soc/spi_struct.h"
|
||||||
|
#include "soc/efuse_reg.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
/* SPI flash controller */
|
||||||
|
#define SPIFLASH SPI1
|
||||||
|
|
||||||
|
/* SPI commands (actual on-wire commands not SPI controller bitmasks)
|
||||||
|
Suitable for use with the execute_flash_command static function.
|
||||||
|
*/
|
||||||
|
#define CMD_RDID 0x9F
|
||||||
|
#define CMD_WRSR 0x01
|
||||||
|
#define CMD_WRSR2 0x31 /* Not all SPI flash uses this command */
|
||||||
|
#define CMD_WREN 0x06
|
||||||
|
#define CMD_WRDI 0x04
|
||||||
|
#define CMD_RDSR 0x05
|
||||||
|
#define CMD_RDSR2 0x35 /* Not all SPI flash uses this command */
|
||||||
|
|
||||||
|
static const char *TAG = "qio_mode";
|
||||||
|
|
||||||
|
typedef unsigned (*read_status_fn_t)();
|
||||||
|
typedef void (*write_status_fn_t)(unsigned);
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
const char *manufacturer;
|
||||||
|
uint8_t mfg_id; /* 8-bit JEDEC manufacturer ID */
|
||||||
|
uint16_t flash_id; /* 16-bit JEDEC flash chip ID */
|
||||||
|
uint16_t id_mask; /* Bits to match on in flash chip ID */
|
||||||
|
read_status_fn_t read_status_fn;
|
||||||
|
write_status_fn_t write_status_fn;
|
||||||
|
uint8_t status_qio_bit;
|
||||||
|
} qio_info_t;
|
||||||
|
|
||||||
|
/* Read 8 bit status using RDSR command */
|
||||||
|
static unsigned read_status_8b_rdsr();
|
||||||
|
/* Read 8 bit status (second byte) using RDSR2 command */
|
||||||
|
static unsigned read_status_8b_rdsr2();
|
||||||
|
/* read 16 bit status using RDSR & RDSR2 (low and high bytes) */
|
||||||
|
static unsigned read_status_16b_rdsr_rdsr2();
|
||||||
|
|
||||||
|
/* Write 8 bit status using WRSR */
|
||||||
|
static void write_status_8b_wrsr(unsigned new_status);
|
||||||
|
/* Write 8 bit status (second byte) using WRSR2 */
|
||||||
|
static void write_status_8b_wrsr2(unsigned new_status);
|
||||||
|
/* Write 16 bit status using WRSR */
|
||||||
|
static void write_status_16b_wrsr(unsigned new_status);
|
||||||
|
|
||||||
|
#define ESP32_D2WD_WP_GPIO 7 /* ESP32-D2WD has this GPIO wired to WP pin of flash */
|
||||||
|
|
||||||
|
#ifndef CONFIG_BOOTLOADER_SPI_WP_PIN // Set in menuconfig if SPI flasher config is set to a quad mode
|
||||||
|
#define CONFIG_BOOTLOADER_SPI_WP_PIN ESP32_D2WD_WP_GPIO
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Array of known flash chips and data to enable Quad I/O mode
|
||||||
|
|
||||||
|
Manufacturer & flash ID can be tested by running "esptool.py
|
||||||
|
flash_id"
|
||||||
|
|
||||||
|
If manufacturer ID matches, and flash ID ORed with flash ID mask
|
||||||
|
matches, enable_qio_mode() will execute "Read Cmd", test if bit
|
||||||
|
number "QIE Bit" is set, and if not set it will call "Write Cmd"
|
||||||
|
with this bit set.
|
||||||
|
|
||||||
|
Searching of this table stops when the first match is found.
|
||||||
|
*/
|
||||||
|
const static qio_info_t chip_data[] = {
|
||||||
|
/* Manufacturer, mfg_id, flash_id, id mask, Read Status, Write Status, QIE Bit */
|
||||||
|
{ "MXIC", 0xC2, 0x2000, 0xFF00, read_status_8b_rdsr, write_status_8b_wrsr, 6 },
|
||||||
|
{ "ISSI", 0x9D, 0x4000, 0xCF00, read_status_8b_rdsr, write_status_8b_wrsr, 6 }, /* IDs 0x40xx, 0x70xx */
|
||||||
|
{ "WinBond", 0xEF, 0x4000, 0xFF00, read_status_16b_rdsr_rdsr2, write_status_16b_wrsr, 9 },
|
||||||
|
{ "GD", 0xC8, 0x6000, 0xFF00, read_status_16b_rdsr_rdsr2, write_status_16b_wrsr, 9 },
|
||||||
|
|
||||||
|
/* Final entry is default entry, if no other IDs have matched.
|
||||||
|
|
||||||
|
This approach works for chips including:
|
||||||
|
GigaDevice (mfg ID 0xC8, flash IDs including 4016),
|
||||||
|
FM25Q32 (QOUT mode only, mfg ID 0xA1, flash IDs including 4016)
|
||||||
|
*/
|
||||||
|
{ NULL, 0xFF, 0xFFFF, 0xFFFF, read_status_8b_rdsr2, write_status_8b_wrsr2, 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NUM_CHIPS (sizeof(chip_data) / sizeof(qio_info_t))
|
||||||
|
|
||||||
|
static esp_err_t enable_qio_mode(read_status_fn_t read_status_fn,
|
||||||
|
write_status_fn_t write_status_fn,
|
||||||
|
uint8_t status_qio_bit);
|
||||||
|
|
||||||
|
/* Generic function to use the "user command" SPI controller functionality
|
||||||
|
to send commands to the SPI flash and read the respopnse.
|
||||||
|
|
||||||
|
The command passed here is always the on-the-wire command given to the SPI flash unit.
|
||||||
|
*/
|
||||||
|
static uint32_t execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len);
|
||||||
|
|
||||||
|
/* dummy_len_plus values defined in ROM for SPI flash configuration */
|
||||||
|
extern uint8_t g_rom_spiflash_dummy_len_plus[];
|
||||||
|
uint32_t bootloader_read_flash_id()
|
||||||
|
{
|
||||||
|
uint32_t id = execute_flash_command(CMD_RDID, 0, 0, 24);
|
||||||
|
id = ((id & 0xff) << 16) | ((id >> 16) & 0xff) | (id & 0xff00);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bootloader_enable_qio_mode(void)
|
||||||
|
{
|
||||||
|
uint32_t raw_flash_id;
|
||||||
|
uint8_t mfg_id;
|
||||||
|
uint16_t flash_id;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Probing for QIO mode enable...");
|
||||||
|
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
|
||||||
|
|
||||||
|
raw_flash_id = g_rom_flashchip.device_id;
|
||||||
|
ESP_LOGD(TAG, "Raw SPI flash chip id 0x%x", raw_flash_id);
|
||||||
|
|
||||||
|
mfg_id = (raw_flash_id >> 16) & 0xFF;
|
||||||
|
flash_id = raw_flash_id & 0xFFFF;
|
||||||
|
ESP_LOGD(TAG, "Manufacturer ID 0x%02x chip ID 0x%04x", mfg_id, flash_id);
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_CHIPS-1; i++) {
|
||||||
|
const qio_info_t *chip = &chip_data[i];
|
||||||
|
if (mfg_id == chip->mfg_id && (flash_id & chip->id_mask) == (chip->flash_id & chip->id_mask)) {
|
||||||
|
ESP_LOGI(TAG, "Enabling QIO for flash chip %s", chip_data[i].manufacturer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == NUM_CHIPS - 1) {
|
||||||
|
ESP_LOGI(TAG, "Enabling default flash chip QIO");
|
||||||
|
}
|
||||||
|
|
||||||
|
enable_qio_mode(chip_data[i].read_status_fn,
|
||||||
|
chip_data[i].write_status_fn,
|
||||||
|
chip_data[i].status_qio_bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t enable_qio_mode(read_status_fn_t read_status_fn,
|
||||||
|
write_status_fn_t write_status_fn,
|
||||||
|
uint8_t status_qio_bit)
|
||||||
|
{
|
||||||
|
uint32_t status;
|
||||||
|
const uint32_t spiconfig = ets_efuse_get_spiconfig();
|
||||||
|
|
||||||
|
if (spiconfig != EFUSE_SPICONFIG_SPI_DEFAULTS && spiconfig != EFUSE_SPICONFIG_HSPI_DEFAULTS) {
|
||||||
|
// spiconfig specifies a custom efuse pin configuration. This config defines all pins -except- WP,
|
||||||
|
// which is compiled into the bootloader instead.
|
||||||
|
//
|
||||||
|
// Most commonly an overriden pin mapping means ESP32-D2WD or ESP32-PICOD4.
|
||||||
|
//Warn if chip is ESP32-D2WD/ESP32-PICOD4 but someone has changed the WP pin
|
||||||
|
//assignment from that chip's WP pin.
|
||||||
|
uint32_t pkg_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG);
|
||||||
|
if (CONFIG_BOOTLOADER_SPI_WP_PIN != ESP32_D2WD_WP_GPIO &&
|
||||||
|
(pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5 ||
|
||||||
|
pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2 ||
|
||||||
|
pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4)) {
|
||||||
|
ESP_LOGW(TAG, "Chip is ESP32-D2WD/ESP32-PICOD4 but flash WP pin is different value to internal flash");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
|
||||||
|
|
||||||
|
status = read_status_fn();
|
||||||
|
ESP_LOGD(TAG, "Initial flash chip status 0x%x", status);
|
||||||
|
|
||||||
|
if ((status & (1<<status_qio_bit)) == 0) {
|
||||||
|
execute_flash_command(CMD_WREN, 0, 0, 0);
|
||||||
|
write_status_fn(status | (1<<status_qio_bit));
|
||||||
|
|
||||||
|
esp_rom_spiflash_wait_idle(&g_rom_flashchip);
|
||||||
|
|
||||||
|
status = read_status_fn();
|
||||||
|
ESP_LOGD(TAG, "Updated flash chip status 0x%x", status);
|
||||||
|
if ((status & (1<<status_qio_bit)) == 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to set QIE bit, not enabling QIO mode");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ESP_LOGD(TAG, "QIO mode already enabled in flash");
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Enabling QIO mode...");
|
||||||
|
|
||||||
|
esp_rom_spiflash_read_mode_t mode;
|
||||||
|
#if CONFIG_FLASHMODE_QOUT
|
||||||
|
mode = ESP_ROM_SPIFLASH_QOUT_MODE;
|
||||||
|
#else
|
||||||
|
mode = ESP_ROM_SPIFLASH_QIO_MODE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
esp_rom_spiflash_config_readmode(mode);
|
||||||
|
|
||||||
|
esp_rom_spiflash_select_qio_pins(CONFIG_BOOTLOADER_SPI_WP_PIN, spiconfig);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned read_status_8b_rdsr()
|
||||||
|
{
|
||||||
|
return execute_flash_command(CMD_RDSR, 0, 0, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned read_status_8b_rdsr2()
|
||||||
|
{
|
||||||
|
return execute_flash_command(CMD_RDSR2, 0, 0, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned read_status_16b_rdsr_rdsr2()
|
||||||
|
{
|
||||||
|
return execute_flash_command(CMD_RDSR, 0, 0, 8) | (execute_flash_command(CMD_RDSR2, 0, 0, 8) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_status_8b_wrsr(unsigned new_status)
|
||||||
|
{
|
||||||
|
execute_flash_command(CMD_WRSR, new_status, 8, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_status_8b_wrsr2(unsigned new_status)
|
||||||
|
{
|
||||||
|
execute_flash_command(CMD_WRSR2, new_status, 8, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_status_16b_wrsr(unsigned new_status)
|
||||||
|
{
|
||||||
|
execute_flash_command(CMD_WRSR, new_status, 16, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t execute_flash_command(uint8_t command, uint32_t mosi_data, uint8_t mosi_len, uint8_t miso_len)
|
||||||
|
{
|
||||||
|
uint32_t old_ctrl_reg = SPIFLASH.ctrl.val;
|
||||||
|
SPIFLASH.ctrl.val = SPI_WP_REG_M; // keep WP high while idle, otherwise leave DIO mode
|
||||||
|
SPIFLASH.user.usr_dummy = 0;
|
||||||
|
SPIFLASH.user.usr_addr = 0;
|
||||||
|
SPIFLASH.user.usr_command = 1;
|
||||||
|
SPIFLASH.user2.usr_command_bitlen = 7;
|
||||||
|
|
||||||
|
SPIFLASH.user2.usr_command_value = command;
|
||||||
|
SPIFLASH.user.usr_miso = miso_len > 0;
|
||||||
|
SPIFLASH.miso_dlen.usr_miso_dbitlen = miso_len ? (miso_len - 1) : 0;
|
||||||
|
SPIFLASH.user.usr_mosi = mosi_len > 0;
|
||||||
|
SPIFLASH.mosi_dlen.usr_mosi_dbitlen = mosi_len ? (mosi_len - 1) : 0;
|
||||||
|
SPIFLASH.data_buf[0] = mosi_data;
|
||||||
|
|
||||||
|
if (g_rom_spiflash_dummy_len_plus[1]) {
|
||||||
|
/* When flash pins are mapped via GPIO matrix, need a dummy cycle before reading via MISO */
|
||||||
|
if (miso_len > 0) {
|
||||||
|
SPIFLASH.user.usr_dummy = 1;
|
||||||
|
SPIFLASH.user1.usr_dummy_cyclelen = g_rom_spiflash_dummy_len_plus[1] - 1;
|
||||||
|
} else {
|
||||||
|
SPIFLASH.user.usr_dummy = 0;
|
||||||
|
SPIFLASH.user1.usr_dummy_cyclelen = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPIFLASH.cmd.usr = 1;
|
||||||
|
while(SPIFLASH.cmd.usr != 0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
SPIFLASH.ctrl.val = old_ctrl_reg;
|
||||||
|
return SPIFLASH.data_buf[0];
|
||||||
|
}
|
204
components/bootloader_support/src/secure_boot.c
Normal file
204
components/bootloader_support/src/secure_boot.c
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "esp_attr.h"
|
||||||
|
#include "esp_types.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#include "rom/cache.h"
|
||||||
|
#include "rom/ets_sys.h"
|
||||||
|
#include "rom/secure_boot.h"
|
||||||
|
|
||||||
|
#include "soc/dport_reg.h"
|
||||||
|
#include "soc/io_mux_reg.h"
|
||||||
|
#include "soc/efuse_reg.h"
|
||||||
|
#include "soc/rtc_cntl_reg.h"
|
||||||
|
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
#include "bootloader_flash.h"
|
||||||
|
#include "bootloader_random.h"
|
||||||
|
#include "esp_image_format.h"
|
||||||
|
#include "esp_secure_boot.h"
|
||||||
|
#include "esp_flash_encrypt.h"
|
||||||
|
#include "esp_efuse.h"
|
||||||
|
|
||||||
|
static const char* TAG = "secure_boot";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function : secure_boot_generate
|
||||||
|
* @description: generate boot digest (aka "abstract") & iv
|
||||||
|
*
|
||||||
|
* @inputs: image_len - length of image to calculate digest for
|
||||||
|
*/
|
||||||
|
static bool secure_boot_generate(uint32_t image_len){
|
||||||
|
esp_err_t err;
|
||||||
|
esp_secure_boot_iv_digest_t digest;
|
||||||
|
const uint32_t *image;
|
||||||
|
|
||||||
|
/* hardware secure boot engine only takes full blocks, so round up the
|
||||||
|
image length. The additional data should all be 0xFF.
|
||||||
|
*/
|
||||||
|
if (image_len % sizeof(digest.iv) != 0) {
|
||||||
|
image_len = (image_len / sizeof(digest.iv) + 1) * sizeof(digest.iv);
|
||||||
|
}
|
||||||
|
ets_secure_boot_start();
|
||||||
|
ets_secure_boot_rd_iv((uint32_t *)digest.iv);
|
||||||
|
ets_secure_boot_hash(NULL);
|
||||||
|
/* iv stored in sec 0 */
|
||||||
|
err = bootloader_flash_erase_sector(0);
|
||||||
|
if (err != ESP_OK)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "SPI erase failed: 0x%x", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* generate digest from image contents */
|
||||||
|
image = bootloader_mmap(ESP_BOOTLOADER_OFFSET, image_len);
|
||||||
|
if (!image) {
|
||||||
|
ESP_LOGE(TAG, "bootloader_mmap(0x1000, 0x%x) failed", image_len);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < image_len; i+= sizeof(digest.iv)) {
|
||||||
|
ets_secure_boot_hash(&image[i/sizeof(uint32_t)]);
|
||||||
|
}
|
||||||
|
bootloader_munmap(image);
|
||||||
|
|
||||||
|
ets_secure_boot_obtain();
|
||||||
|
ets_secure_boot_rd_abstract((uint32_t *)digest.digest);
|
||||||
|
ets_secure_boot_finish();
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "write iv+digest to flash");
|
||||||
|
err = bootloader_flash_write(FLASH_OFFS_SECURE_BOOT_IV_DIGEST, &digest,
|
||||||
|
sizeof(digest), esp_flash_encryption_enabled());
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "SPI write failed: 0x%x", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Cache_Read_Enable(0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Burn values written to the efuse write registers */
|
||||||
|
static inline void burn_efuses()
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_SECURE_BOOT_TEST_MODE
|
||||||
|
ESP_LOGE(TAG, "SECURE BOOT TEST MODE. Not really burning any efuses! NOT SECURE");
|
||||||
|
#else
|
||||||
|
esp_efuse_burn_new_values();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_secure_boot_permanently_enable(void) {
|
||||||
|
esp_err_t err;
|
||||||
|
uint32_t image_len = 0;
|
||||||
|
if (esp_secure_boot_enabled())
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "bootloader secure boot is already enabled, continuing..");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = esp_image_verify_bootloader(&image_len);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "bootloader image appears invalid! error %d", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t dis_reg = REG_READ(EFUSE_BLK0_RDATA0_REG);
|
||||||
|
bool efuse_key_read_protected = dis_reg & EFUSE_RD_DIS_BLK2;
|
||||||
|
bool efuse_key_write_protected = dis_reg & EFUSE_WR_DIS_BLK2;
|
||||||
|
if (efuse_key_read_protected == false
|
||||||
|
&& efuse_key_write_protected == false
|
||||||
|
&& REG_READ(EFUSE_BLK2_RDATA0_REG) == 0
|
||||||
|
&& REG_READ(EFUSE_BLK2_RDATA1_REG) == 0
|
||||||
|
&& REG_READ(EFUSE_BLK2_RDATA2_REG) == 0
|
||||||
|
&& REG_READ(EFUSE_BLK2_RDATA3_REG) == 0
|
||||||
|
&& REG_READ(EFUSE_BLK2_RDATA4_REG) == 0
|
||||||
|
&& REG_READ(EFUSE_BLK2_RDATA5_REG) == 0
|
||||||
|
&& REG_READ(EFUSE_BLK2_RDATA6_REG) == 0
|
||||||
|
&& REG_READ(EFUSE_BLK2_RDATA7_REG) == 0) {
|
||||||
|
ESP_LOGI(TAG, "Generating new secure boot key...");
|
||||||
|
uint32_t buf[8];
|
||||||
|
bootloader_fill_random(buf, sizeof(buf));
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
ESP_LOGV(TAG, "EFUSE_BLK2_WDATA%d_REG = 0x%08x", i, buf[i]);
|
||||||
|
REG_WRITE(EFUSE_BLK2_WDATA0_REG + 4*i, buf[i]);
|
||||||
|
}
|
||||||
|
bzero(buf, sizeof(buf));
|
||||||
|
burn_efuses();
|
||||||
|
ESP_LOGI(TAG, "Read & write protecting new key...");
|
||||||
|
REG_WRITE(EFUSE_BLK0_WDATA0_REG, EFUSE_WR_DIS_BLK2 | EFUSE_RD_DIS_BLK2);
|
||||||
|
burn_efuses();
|
||||||
|
efuse_key_read_protected = true;
|
||||||
|
efuse_key_write_protected = true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "Using pre-loaded secure boot key in EFUSE block 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Generating secure boot digest...");
|
||||||
|
if (false == secure_boot_generate(image_len)){
|
||||||
|
ESP_LOGE(TAG, "secure boot generation failed");
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Digest generation complete.");
|
||||||
|
|
||||||
|
#ifndef CONFIG_SECURE_BOOT_TEST_MODE
|
||||||
|
if (!efuse_key_read_protected) {
|
||||||
|
ESP_LOGE(TAG, "Pre-loaded key is not read protected. Refusing to blow secure boot efuse.");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
if (!efuse_key_write_protected) {
|
||||||
|
ESP_LOGE(TAG, "Pre-loaded key is not write protected. Refusing to blow secure boot efuse.");
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "blowing secure boot efuse...");
|
||||||
|
ESP_LOGD(TAG, "before updating, EFUSE_BLK0_RDATA6 %x", REG_READ(EFUSE_BLK0_RDATA6_REG));
|
||||||
|
|
||||||
|
uint32_t new_wdata6 = EFUSE_RD_ABS_DONE_0;
|
||||||
|
|
||||||
|
#ifndef CONFIG_SECURE_BOOT_ALLOW_JTAG
|
||||||
|
ESP_LOGI(TAG, "Disable JTAG...");
|
||||||
|
new_wdata6 |= EFUSE_RD_DISABLE_JTAG;
|
||||||
|
#else
|
||||||
|
ESP_LOGW(TAG, "Not disabling JTAG - SECURITY COMPROMISED");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC
|
||||||
|
ESP_LOGI(TAG, "Disable ROM BASIC interpreter fallback...");
|
||||||
|
new_wdata6 |= EFUSE_RD_CONSOLE_DEBUG_DISABLE;
|
||||||
|
#else
|
||||||
|
ESP_LOGW(TAG, "Not disabling ROM BASIC fallback - SECURITY COMPROMISED");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
REG_WRITE(EFUSE_BLK0_WDATA6_REG, new_wdata6);
|
||||||
|
burn_efuses();
|
||||||
|
uint32_t after = REG_READ(EFUSE_BLK0_RDATA6_REG);
|
||||||
|
ESP_LOGD(TAG, "after updating, EFUSE_BLK0_RDATA6 %x", after);
|
||||||
|
if (after & EFUSE_RD_ABS_DONE_0) {
|
||||||
|
ESP_LOGI(TAG, "secure boot is now enabled for bootloader image");
|
||||||
|
return ESP_OK;
|
||||||
|
} else {
|
||||||
|
#ifdef CONFIG_SECURE_BOOT_TEST_MODE
|
||||||
|
ESP_LOGE(TAG, "secure boot not enabled due to test mode");
|
||||||
|
#else
|
||||||
|
ESP_LOGE(TAG, "secure boot not enabled for bootloader image, EFUSE_RD_ABS_DONE_0 is probably write protected!");
|
||||||
|
#endif
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
}
|
93
components/bootloader_support/src/secure_boot_signatures.c
Normal file
93
components/bootloader_support/src/secure_boot_signatures.c
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
#include "bootloader_flash.h"
|
||||||
|
#include "bootloader_sha.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_image_format.h"
|
||||||
|
#include "esp_secure_boot.h"
|
||||||
|
|
||||||
|
#include "uECC.h"
|
||||||
|
|
||||||
|
#ifdef BOOTLOADER_BUILD
|
||||||
|
#include "rom/sha.h"
|
||||||
|
typedef SHA_CTX sha_context;
|
||||||
|
#else
|
||||||
|
#include "hwcrypto/sha.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char* TAG = "secure_boot";
|
||||||
|
|
||||||
|
extern const uint8_t signature_verification_key_start[] asm("_binary_signature_verification_key_bin_start");
|
||||||
|
extern const uint8_t signature_verification_key_end[] asm("_binary_signature_verification_key_bin_end");
|
||||||
|
|
||||||
|
#define SIGNATURE_VERIFICATION_KEYLEN 64
|
||||||
|
|
||||||
|
#define DIGEST_LEN 32
|
||||||
|
|
||||||
|
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
||||||
|
{
|
||||||
|
uint8_t digest[DIGEST_LEN];
|
||||||
|
const uint8_t *data;
|
||||||
|
const esp_secure_boot_sig_block_t *sigblock;
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length);
|
||||||
|
|
||||||
|
data = bootloader_mmap(src_addr, length + sizeof(esp_secure_boot_sig_block_t));
|
||||||
|
if(data == NULL) {
|
||||||
|
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", src_addr, length+sizeof(esp_secure_boot_sig_block_t));
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate digest of main image
|
||||||
|
#ifdef BOOTLOADER_BUILD
|
||||||
|
bootloader_sha256_handle_t handle = bootloader_sha256_start();
|
||||||
|
bootloader_sha256_data(handle, data, length);
|
||||||
|
bootloader_sha256_finish(handle, digest);
|
||||||
|
#else
|
||||||
|
/* Use thread-safe esp-idf SHA function */
|
||||||
|
esp_sha(SHA2_256, data, length, digest);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Map the signature block and verify the signature
|
||||||
|
sigblock = (const esp_secure_boot_sig_block_t *)(data + length);
|
||||||
|
esp_err_t err = esp_secure_boot_verify_signature_block(sigblock, digest);
|
||||||
|
bootloader_munmap(data);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest)
|
||||||
|
{
|
||||||
|
ptrdiff_t keylen;
|
||||||
|
bool is_valid;
|
||||||
|
|
||||||
|
keylen = signature_verification_key_end - signature_verification_key_start;
|
||||||
|
if(keylen != SIGNATURE_VERIFICATION_KEYLEN) {
|
||||||
|
ESP_LOGE(TAG, "Embedded public verification key has wrong length %d", keylen);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sig_block->version != 0) {
|
||||||
|
ESP_LOGE(TAG, "image has invalid signature version field 0x%08x", sig_block->version);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_valid = uECC_verify(signature_verification_key_start,
|
||||||
|
image_digest,
|
||||||
|
DIGEST_LEN,
|
||||||
|
sig_block->signature,
|
||||||
|
uECC_secp256r1());
|
||||||
|
return is_valid ? ESP_OK : ESP_ERR_IMAGE_INVALID;
|
||||||
|
}
|
4
components/bootloader_support/test/component.mk
Normal file
4
components/bootloader_support/test/component.mk
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#
|
||||||
|
#Component Makefile
|
||||||
|
#
|
||||||
|
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
94
components/bootloader_support/test/test_verify_image.c
Normal file
94
components/bootloader_support/test/test_verify_image.c
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Tests for bootloader_support esp_load(ESP_IMAGE_VERIFY, ...)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <esp_types.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "string.h"
|
||||||
|
#include "rom/ets_sys.h"
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
#include "freertos/queue.h"
|
||||||
|
#include "freertos/xtensa_api.h"
|
||||||
|
#include "unity.h"
|
||||||
|
#include "bootloader_common.h"
|
||||||
|
#include "esp_partition.h"
|
||||||
|
#include "esp_ota_ops.h"
|
||||||
|
#include "esp_image_format.h"
|
||||||
|
|
||||||
|
TEST_CASE("Verify bootloader image in flash", "[bootloader_support]")
|
||||||
|
{
|
||||||
|
const esp_partition_pos_t fake_bootloader_partition = {
|
||||||
|
.offset = ESP_BOOTLOADER_OFFSET,
|
||||||
|
.size = ESP_PARTITION_TABLE_OFFSET - ESP_BOOTLOADER_OFFSET,
|
||||||
|
};
|
||||||
|
esp_image_metadata_t data = { 0 };
|
||||||
|
TEST_ASSERT_EQUAL_HEX(ESP_OK, esp_image_load(ESP_IMAGE_VERIFY, &fake_bootloader_partition, &data));
|
||||||
|
TEST_ASSERT_NOT_EQUAL(0, data.image_len);
|
||||||
|
|
||||||
|
uint32_t bootloader_length = 0;
|
||||||
|
TEST_ASSERT_EQUAL_HEX(ESP_OK, esp_image_verify_bootloader(&bootloader_length));
|
||||||
|
TEST_ASSERT_EQUAL(data.image_len, bootloader_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Verify unit test app image", "[bootloader_support]")
|
||||||
|
{
|
||||||
|
esp_image_metadata_t data = { 0 };
|
||||||
|
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||||
|
TEST_ASSERT_NOT_EQUAL(NULL, running);
|
||||||
|
const esp_partition_pos_t running_pos = {
|
||||||
|
.offset = running->address,
|
||||||
|
.size = running->size,
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_HEX(ESP_OK, esp_image_load(ESP_IMAGE_VERIFY, &running_pos, &data));
|
||||||
|
TEST_ASSERT_NOT_EQUAL(0, data.image_len);
|
||||||
|
TEST_ASSERT_TRUE(data.image_len <= running->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_label_search (int num_test, const char *list, const char *t_label, bool result)
|
||||||
|
{
|
||||||
|
// gen_esp32part.py trims up to 16 characters
|
||||||
|
// and the string may not have a null terminal symbol.
|
||||||
|
// below is cutting as it does the generator.
|
||||||
|
char label[16 + 1] = {0};
|
||||||
|
strncpy(label, t_label, sizeof(label) - 1);
|
||||||
|
|
||||||
|
bool ret = bootloader_common_label_search(list, label);
|
||||||
|
if (ret != result) {
|
||||||
|
printf("%d) %s | %s \n", num_test, list, label);
|
||||||
|
}
|
||||||
|
TEST_ASSERT_MESSAGE(ret == result, "Test failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test label_search", "[bootloader_support]")
|
||||||
|
{
|
||||||
|
TEST_ASSERT_FALSE(bootloader_common_label_search(NULL, NULL));
|
||||||
|
TEST_ASSERT_FALSE(bootloader_common_label_search("nvs", NULL));
|
||||||
|
|
||||||
|
check_label_search(1, "nvs", "nvs", true);
|
||||||
|
check_label_search(2, "nvs, ", "nvs", true);
|
||||||
|
check_label_search(3, "nvs1", "nvs", false);
|
||||||
|
check_label_search(3, "nvs1, ", "nvs", false);
|
||||||
|
check_label_search(4, "nvs1nvs1, phy", "nvs1", false);
|
||||||
|
check_label_search(5, "nvs1, nvs1, phy", "nvs1", true);
|
||||||
|
check_label_search(6, "nvs12, nvs12, phy", "nvs1", false);
|
||||||
|
check_label_search(7, "nvs12, nvs1, phy", "nvs1", true);
|
||||||
|
check_label_search(8, "nvs12, nvs3, phy, nvs1","nvs1", true);
|
||||||
|
check_label_search(9, "nvs1nvs1, phy, nvs", "nvs", true);
|
||||||
|
check_label_search(10, "nvs1nvs1, phy, nvs1", "nvs", false);
|
||||||
|
check_label_search(11, "nvs1, nvs, phy, nvs1", "nvs", true);
|
||||||
|
check_label_search(12, "nvs1, nvs2, phy, nvs","nvs", true);
|
||||||
|
check_label_search(13, "ota_data, backup_nvs", "nvs", false);
|
||||||
|
check_label_search(14, "nvs1, nvs2, ota, nvs", "vs1", false);
|
||||||
|
|
||||||
|
check_label_search(20, "12345678901234, phy, nvs1", "12345678901234", true);
|
||||||
|
check_label_search(21, "123456789012345, phy, nvs1", "123456789012345", true);
|
||||||
|
check_label_search(22, "1234567890123456, phy, nvs1", "1234567890123456", true);
|
||||||
|
check_label_search(23, "12345678901234567, phy, nvs1", "12345678901234567", false);
|
||||||
|
check_label_search(24, "1234567890123456, phy, nvs1", "12345678901234567", true);
|
||||||
|
check_label_search(25, "phy, 1234567890123456, nvs1", "12345678901234567", true);
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user