feat(bootloader_support): add bootloader support from idf

idf commit: a3a0b01c
This commit is contained in:
Wu Jian Gang
2018-06-17 23:46:34 +08:00
committed by Dong Heng
parent db10e49f14
commit 5ca0b194ef
33 changed files with 4500 additions and 0 deletions

View 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
}

View 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;
}

View 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

View 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) {}
}

View 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);
}

View 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

View 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)();
}

View 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();
}
}

View 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
}

View 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;
}

View 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;
}

View 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];
}

View 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;
}
}

View 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;
}