From b54f0edff4fece1736cc247e40ba67867fc7bcc1 Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Tue, 27 Oct 2020 15:37:06 +0800 Subject: [PATCH] xtensa/esp32: Add Partition and OTA device --- arch/xtensa/src/esp32/Kconfig | 21 + arch/xtensa/src/esp32/Make.defs | 4 + arch/xtensa/src/esp32/esp32_partition.c | 676 ++++++++++++++++++ arch/xtensa/src/esp32/esp32_partition.h | 72 ++ .../esp32/esp32-core/src/esp32_bringup.c | 11 + 5 files changed, 784 insertions(+) create mode 100644 arch/xtensa/src/esp32/esp32_partition.c create mode 100644 arch/xtensa/src/esp32/esp32_partition.h diff --git a/arch/xtensa/src/esp32/Kconfig b/arch/xtensa/src/esp32/Kconfig index e4836d56b6..1c92d7b18a 100644 --- a/arch/xtensa/src/esp32/Kconfig +++ b/arch/xtensa/src/esp32/Kconfig @@ -29,6 +29,14 @@ config ESP32_RT_TIMER bool "Real-time Timer" default n +config ESP32_PARTITION + bool "ESP32 Partition" + default n + select ESP32_SPIFLASH + ---help--- + Decode esp-idf's partition file and initialize + partition by nuttx MTD. + menu "ESP32 Peripheral Selection" config ESP32_UART @@ -688,4 +696,17 @@ config ESP32_RT_TIMER_TASK_STACK_SIZE endmenu # Real-Time Timer +menu "Partition Configuration" + depends on ESP32_PARTITION + +config ESP32_PARTITION_OFFSET + hex "Partition offset" + default "0x8000" + +config ESP32_PARTITION_MOUNT + string "Partition mount point" + default "/dev/esp/partition/" + +endmenu # ESP32_PARTITION + endif # ARCH_CHIP_ESP32 diff --git a/arch/xtensa/src/esp32/Make.defs b/arch/xtensa/src/esp32/Make.defs index a43f537b69..f214634b25 100644 --- a/arch/xtensa/src/esp32/Make.defs +++ b/arch/xtensa/src/esp32/Make.defs @@ -153,6 +153,10 @@ ifeq ($(CONFIG_XTENSA_IMEM_PROCFS),y) CHIP_CSRCS += esp32_procfs_imm.c endif +ifeq ($(CONFIG_ESP32_PARTITION),y) +CHIP_CSRCS += esp32_partition.c +endif + ifeq ($(CONFIG_ARCH_USE_MODULE_TEXT),y) CHIP_CSRCS += esp32_modtext.c CMN_ASRCS += xtensa_loadstore.S diff --git a/arch/xtensa/src/esp32/esp32_partition.c b/arch/xtensa/src/esp32/esp32_partition.c new file mode 100644 index 0000000000..1ba1965d59 --- /dev/null +++ b/arch/xtensa/src/esp32/esp32_partition.c @@ -0,0 +1,676 @@ +/**************************************************************************** + * arch/xtensa/src/esp32/esp32_partition.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "esp32_spiflash.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Partition table max size */ + +#define PARTITION_MAX_SIZE (0xc00) + +/* Partition table header magic value */ + +#define PARTITION_MAGIC (0x50aa) + +/* Partition table member label length */ + +#define PARTITION_LABEL_LEN (16) + +/* OTA data offset in OTA partition */ + +#define OTA_DATA_OFFSET (4096) + +/* OTA data number */ + +#define OTA_DATA_NUM (2) + +/* Partition offset in SPI Flash */ + +#define ESP32_PARTITION_OFFSET CONFIG_ESP32_PARTITION_OFFSET + +/* Partition MTD device mount point */ + +#define ESP32_PARTITION_MOUNT CONFIG_ESP32_PARTITION_MOUNT + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* OTA image operation code */ + +enum ota_img_ctrl +{ + OTA_IMG_GET_BOOT = 0xe1, + OTA_IMG_SET_BOOT = 0xe2 +}; + +/* OTA image state */ + +enum ota_img_state +{ + /** + * Monitor the first boot. In bootloader of esp-idf this state is changed + * to ESP_OTA_IMG_PENDING_VERIFY if this bootloader enable app rollback. + * + * So this driver doesn't use this state currently. + */ + + OTA_IMG_NEW = 0x0, + + /** + * First boot for this app was. If while the second boot this state is then + * it will be changed to ABORTED if this bootloader enable app rollback. + * + * So this driver doesn't use this state currently. + */ + + OTA_IMG_PENDING_VERIFY = 0x1, + + /* App was confirmed as workable. App can boot and work without limits. */ + + OTA_IMG_VALID = 0x2, + + /* App was confirmed as non-workable. This app will not selected to boot. */ + + OTA_IMG_INVALID = 0x3, + + /** + * App could not confirm the workable or non-workable. In bootloader + * IMG_PENDING_VERIFY state will be changed to IMG_ABORTED. This app will + * not selected to boot at all if this bootloader enable app rollback. + * + * So this driver doesn't use this state currently. + */ + + OTA_IMG_ABORTED = 0x4, + + /** + * Undefined. App can boot and work without limits in esp-idf. + * + * This state is not used. + */ + + OTA_IMG_UNDEFINED = 0xffffffff, +}; + +/* OTA image boot sequency */ + +enum ota_img_bootseq +{ + OTA_IMG_BOOT_FACTORY = 0, + OTA_IMG_BOOT_OTA_0 = 1, + OTA_IMG_BOOT_OTA_1 = 2, + OTA_IMG_BOOT_SEQ_MAX +}; + +/* Partition information data */ + +struct partition_info_priv +{ + uint16_t magic; /* Partition magic */ + uint8_t type; /* Partition type */ + uint8_t subtype; /* Partition sub-type */ + + uint32_t offset; /* Offset in SPI Flash */ + uint32_t size; /* Size by byte */ + + uint8_t label[PARTITION_LABEL_LEN]; /* Partition label */ + + uint32_t flags; /* Partition flags */ +}; + +/* Partition device data */ + +struct mtd_dev_priv +{ + struct mtd_dev_s mtd; /* MTD data */ + + uint8_t type; /* Partition type */ + uint8_t subtype; /* Partition sub-type */ + uint32_t flags; /* Partition flags */ + + struct mtd_dev_s *ll_mtd; /* Low-level MTD data */ + struct mtd_dev_s *part_mtd; /* Partition MTD data */ +}; + +/* OTA data entry */ + +struct ota_data_entry +{ + uint32_t ota_seq; /* Boot sequence */ + uint8_t seq_label[20]; /* Boot sequence label */ + uint32_t ota_state; /* Boot entry state */ + uint32_t crc; /* Boot ota_seq CRC32 */ +}; + +/**************************************************************************** + * External Function Prototypes + ****************************************************************************/ + +extern uint32_t crc32_le(uint32_t crc, uint8_t const *buf, uint32_t len); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ota_is_valid + * + * Description: + * Check if OTA data is valid + * + * Input Parameters: + * ota_data - OTA data + * + * Returned Value: + * true if checking success or false if fail + * + ****************************************************************************/ + +static bool ota_is_valid(struct ota_data_entry *ota_data) +{ + if ((ota_data->ota_seq >= OTA_IMG_BOOT_SEQ_MAX) || + (ota_data->ota_state != OTA_IMG_VALID) || + (ota_data->crc != crc32_le(UINT32_MAX, (uint8_t *)ota_data, 4))) + { + return false; + } + + return true; +} + +/**************************************************************************** + * Name: ota_get_bootseq + * + * Description: + * Get boot sequence + * + * Input Parameters: + * dev - Partition private MTD data + * num - boot sequence buffer + * + * Returned Value: + * 0 if success or a negative value if fail. + * + ****************************************************************************/ + +static int ota_get_bootseq(struct mtd_dev_priv *dev, int *num) +{ + int i; + int ret; + struct ota_data_entry ota_data; + int size = sizeof(struct ota_data_entry); + + /* Each OTA data locates in independent sector */ + + for (i = 0; i < OTA_DATA_NUM; i++) + { + ret = MTD_READ(dev->part_mtd, i * OTA_DATA_OFFSET, + size, (uint8_t *)&ota_data); + if (ret != size) + { + ferr("ERROR: Failed to read OTA%d data error=%d\n", i, ret); + return -1; + } + + if (ota_is_valid(&ota_data)) + { + *num = i + OTA_IMG_BOOT_OTA_0; + break; + } + } + + if (i >= 2) + { + *num = OTA_IMG_BOOT_FACTORY; + } + + return 0; +} + +/**************************************************************************** + * Name: ota_set_bootseq + * + * Description: + * Set boot sequence + * + * Input Parameters: + * dev - Partition private MTD data + * num - boot sequence buffer + * + * Returned Value: + * 0 if success or a negative value if fail. + * + ****************************************************************************/ + +static int ota_set_bootseq(struct mtd_dev_priv *dev, int num) +{ + int ret; + int id; + int old_id; + struct ota_data_entry ota_data; + int size = sizeof(struct ota_data_entry); + + finfo("INFO: num=%d\n", num); + + switch (num) + { + case OTA_IMG_BOOT_FACTORY: + + /* Erase all OTA data to force use factory app */ + + ret = MTD_ERASE(dev->part_mtd, 0, OTA_DATA_NUM); + if (ret != OTA_DATA_NUM) + { + ferr("ERROR: Failed to erase OTA data error=%d\n", ret); + return -1; + } + + break; + case OTA_IMG_BOOT_OTA_0: + case OTA_IMG_BOOT_OTA_1: + { + id = num - 1; + old_id = num == OTA_IMG_BOOT_OTA_0 ? OTA_IMG_BOOT_OTA_1 - 1: + OTA_IMG_BOOT_OTA_0 - 1; + + ret = MTD_ERASE(dev->part_mtd, id, 1); + if (ret != 1) + { + ferr("ERROR: Failed to erase OTA%d data error=%d\n", id, ret); + return -1; + } + + ota_data.ota_state = OTA_IMG_VALID; + ota_data.ota_seq = num; + ota_data.crc = crc32_le(UINT32_MAX, (uint8_t *)&ota_data, 4); + ret = MTD_WRITE(dev->part_mtd, id * OTA_DATA_OFFSET, + size, (uint8_t *)&ota_data); + if (ret != size) + { + ferr("ERROR: Failed to write OTA%d data error=%d\n", + id, ret); + return -1; + } + + /* Erase old OTA data to force new OTA bin */ + + ret = MTD_ERASE(dev->part_mtd, old_id, 1); + if (ret != 1) + { + ferr("ERROR: Failed to erase OTA%d data error=%d\n", + old_id, ret); + return -1; + } + } + + break; + default: + ferr("ERROR: num=%d is error\n", num); + return -EINVAL; + } + + return 0; +} + +/**************************************************************************** + * Name: esp32_part_erase + * + * Description: + * Erase SPI Flash designated sectors. + * + * Input Parameters: + * dev - ESP32 MTD device data + * startblock - start block number, it is not equal to SPI Flash's block + * nblocks - blocks number + * + * Returned Value: + * 0 if success or a negative value if fail. + * + ****************************************************************************/ + +static int esp32_part_erase(FAR struct mtd_dev_s *dev, off_t startblock, + size_t nblocks) +{ + struct mtd_dev_priv *mtd_priv = (struct mtd_dev_priv *)dev; + + return MTD_ERASE(mtd_priv->ll_mtd, startblock, nblocks); +} + +/**************************************************************************** + * Name: esp32_part_read + * + * Description: + * Read data from SPI Flash at designated address. + * + * Input Parameters: + * dev - ESP32 MTD device data + * offset - target address offset + * nbytes - data number + * buffer - data buffer pointer + * + * Returned Value: + * Read data bytes if success or a negative value if fail. + * + ****************************************************************************/ + +static ssize_t esp32_part_read(FAR struct mtd_dev_s *dev, off_t offset, + size_t nbytes, FAR uint8_t *buffer) +{ + struct mtd_dev_priv *mtd_priv = (struct mtd_dev_priv *)dev; + + return MTD_READ(mtd_priv->ll_mtd, offset, nbytes, buffer); +} + +/**************************************************************************** + * Name: esp32_part_bread + * + * Description: + * Read data from designated blocks. + * + * Input Parameters: + * dev - ESP32 MTD device data + * startblock - start block number, it is not equal to SPI Flash's block + * nblocks - blocks number + * buffer - data buffer pointer + * + * Returned Value: + * Read block number if success or a negative value if fail. + * + ****************************************************************************/ + +static ssize_t esp32_part_bread(FAR struct mtd_dev_s *dev, off_t startblock, + size_t nblocks, FAR uint8_t *buffer) +{ + struct mtd_dev_priv *mtd_priv = (struct mtd_dev_priv *)dev; + + return MTD_BREAD(mtd_priv->ll_mtd, startblock, nblocks, buffer); +} + +/**************************************************************************** + * Name: esp32_part_write + * + * Description: + * write data to SPI Flash at designated address. + * + * Input Parameters: + * dev - ESP32 MTD device data + * offset - target address offset + * nbytes - data number + * buffer - data buffer pointer + * + * Returned Value: + * Writen bytes if success or a negative value if fail. + * + ****************************************************************************/ + +static ssize_t esp32_part_write(FAR struct mtd_dev_s *dev, off_t offset, + size_t nbytes, FAR const uint8_t *buffer) +{ + struct mtd_dev_priv *mtd_priv = (struct mtd_dev_priv *)dev; + + return MTD_WRITE(mtd_priv->ll_mtd, offset, nbytes, buffer); +} + +/**************************************************************************** + * Name: esp32_part_bwrite + * + * Description: + * Write data to designated blocks. + * + * Input Parameters: + * dev - ESP32 MTD device data + * startblock - start MTD block number, + * it is not equal to SPI Flash's block + * nblocks - blocks number + * buffer - data buffer pointer + * + * Returned Value: + * Writen block number if success or a negative value if fail. + * + ****************************************************************************/ + +static ssize_t esp32_part_bwrite(FAR struct mtd_dev_s *dev, off_t startblock, + size_t nblocks, FAR const uint8_t *buffer) +{ + struct mtd_dev_priv *mtd_priv = (struct mtd_dev_priv *)dev; + + return MTD_BWRITE(mtd_priv->ll_mtd, startblock, nblocks, buffer); +} + +/**************************************************************************** + * Name: esp32_part_ioctl + * + * Description: + * Set/Get option to/from ESP32 SPI Flash MTD device data. + * + * Input Parameters: + * dev - ESP32 MTD device data + * cmd - operation command + * arg - operation argument + * + * Returned Value: + * 0 if success or a negative value if fail. + * + ****************************************************************************/ + +static int esp32_part_ioctl(FAR struct mtd_dev_s *dev, int cmd, + unsigned long arg) +{ + int ret; + struct mtd_dev_priv *mtd_priv = (struct mtd_dev_priv *)dev; + + finfo("INFO: cmd=%d(%x) arg=%x\n", cmd, cmd, arg); + + if (!_MTDIOCVALID(cmd)) + { + ferr("ERROR: cmd=%d(%x) is error\n", cmd, cmd); + return -EINVAL; + } + + switch (_IOC_NR(cmd)) + { + case OTA_IMG_GET_BOOT: + { + int *num = (int *)arg; + + ret = ota_get_bootseq(mtd_priv, num); + if (ret) + { + ferr("ERROR: Failed to get boot img\n"); + } + } + + break; + case OTA_IMG_SET_BOOT: + { + ret = ota_set_bootseq(mtd_priv, arg); + if (ret) + { + ferr("ERROR: Failed to set boot img\n"); + } + } + + break; + default: + { + ret = MTD_IOCTL(mtd_priv->ll_mtd, cmd, arg); + } + + break; + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32_partition_init + * + * Description: + * Initialize ESP32 partition. Read partition information of esp-idf, + * and create MTD by these data + * + * Input Parameters: + * None + * + * Returned Value: + * 0 if success or a negative value if fail. + * + ****************************************************************************/ + +int esp32_partition_init(void) +{ + int i; + struct partition_info_priv *info; + uint8_t *pbuf; + struct mtd_dev_s *mtd; + struct mtd_dev_s *mtd_part; + struct mtd_geometry_s geo; + struct mtd_dev_priv *mtd_priv; + int ret = 0; + const int num = PARTITION_MAX_SIZE / sizeof(struct partition_info_priv); + const char path_base[] = ESP32_PARTITION_MOUNT; + char lable[PARTITION_LABEL_LEN + 1]; + char path[PARTITION_LABEL_LEN + sizeof(path_base)]; + + pbuf = kmm_malloc(PARTITION_MAX_SIZE); + if (!pbuf) + { + ferr("ERROR: Failed to allocate %d byte\n", PARTITION_MAX_SIZE); + ret = -1; + goto errout_with_malloc; + } + + mtd = esp32_spiflash_get_mtd(); + if (!mtd) + { + ferr("ERROR: Failed to get SPI flash MTD\n"); + ret = -1; + goto errout_with_mtd; + } + + ret = MTD_IOCTL(mtd, MTDIOC_GEOMETRY, (unsigned long)&geo); + if (ret) + { + ferr("ERROR: Failed to get info from MTD\n"); + ret = -1; + goto errout_with_mtd; + } + + ret = MTD_READ(mtd, ESP32_PARTITION_OFFSET, + PARTITION_MAX_SIZE, pbuf); + if (ret != PARTITION_MAX_SIZE) + { + ferr("ERROR: Failed to get read data from MTD\n"); + ret = -1; + goto errout_with_mtd; + } + + info = (struct partition_info_priv *)pbuf; + for (i = 0; i < num; i++) + { + if (info->magic != PARTITION_MAGIC) + { + break; + } + + strncpy(lable, (char *)info->label, PARTITION_LABEL_LEN); + lable[PARTITION_LABEL_LEN] = 0; + sprintf(path, "%s%s", path_base, lable); + + finfo("INFO: [label]: %s\n", lable); + finfo("INFO: [type]: %d\n", info->type); + finfo("INFO: [subtype]: %d\n", info->subtype); + finfo("INFO: [offset]: 0x%08x\n", info->offset); + finfo("INFO: [size]: 0x%08x\n", info->size); + finfo("INFO: [flags]: 0x%08x\n", info->flags); + finfo("INFO: [mount]: %s\n", path); + + mtd_priv = kmm_malloc(sizeof(struct mtd_dev_priv)); + if (!mtd_priv) + { + ferr("ERROR: Failed to allocate %d byte\n", + sizeof(struct mtd_dev_priv)); + ret = -1; + goto errout_with_mtd; + } + + mtd_priv->ll_mtd = mtd; + mtd_priv->mtd.bread = esp32_part_bread; + mtd_priv->mtd.bwrite = esp32_part_bwrite; + mtd_priv->mtd.erase = esp32_part_erase; + mtd_priv->mtd.ioctl = esp32_part_ioctl; + mtd_priv->mtd.read = esp32_part_read; + mtd_priv->mtd.write = esp32_part_write; + mtd_priv->mtd.name = lable; + + mtd_part = mtd_partition(&mtd_priv->mtd, + info->offset / geo.blocksize, + info->size / geo.blocksize); + if (!mtd_part) + { + ferr("ERROR: Failed to create MTD partition\n"); + kmm_free(mtd_priv); + ret = -1; + goto errout_with_mtd; + } + + mtd_priv->part_mtd = mtd_part; + + ret = register_mtddriver(path, mtd_part, 0777, NULL); + if (ret < 0) + { + ferr("ERROR: Failed to regitser MTD @ %s\n", path); + kmm_free(mtd_priv); + ret = -1; + goto errout_with_mtd; + } + + info++; + } + + ret = 0; + +errout_with_mtd: + kmm_free(pbuf); + +errout_with_malloc: + return ret; +} diff --git a/arch/xtensa/src/esp32/esp32_partition.h b/arch/xtensa/src/esp32/esp32_partition.h new file mode 100644 index 0000000000..eb114a1ae3 --- /dev/null +++ b/arch/xtensa/src/esp32/esp32_partition.h @@ -0,0 +1,72 @@ +/**************************************************************************** + * arch/xtensa/src/esp32/esp32_partition.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_XTENSA_SRC_ESP32_ESP32_PARTITION_H +#define __ARCH_XTENSA_SRC_ESP32_ESP32_PARTITION_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: esp32_partition_init + * + * Description: + * Initialize ESP32 partition. Read partition information of esp-idf, + * and create MTD by these data + * + * Input Parameters: + * None + * + * Returned Value: + * 0 if success or a negative value if fail. + * + ****************************************************************************/ + +int esp32_partition_init(void); + +#ifdef __cplusplus +} +#endif +#undef EXTERN + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_XTENSA_SRC_ESP32_ESP32_PARTITION_H */ diff --git a/boards/xtensa/esp32/esp32-core/src/esp32_bringup.c b/boards/xtensa/esp32/esp32-core/src/esp32_bringup.c index 9723d3ef89..c0107248bb 100644 --- a/boards/xtensa/esp32/esp32-core/src/esp32_bringup.c +++ b/boards/xtensa/esp32/esp32-core/src/esp32_bringup.c @@ -66,6 +66,7 @@ #include "esp32_wlan.h" #include "esp32_spiflash.h" +#include "esp32_partition.h" /**************************************************************************** * Pre-processor Definitions @@ -184,6 +185,16 @@ int esp32_bringup(void) } #endif +#ifdef CONFIG_ESP32_PARTITION + ret = esp32_partition_init(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Failed to initialize partition error=%d\n", + ret); + return ret; + } +#endif + #ifdef CONFIG_ESP32_WIRELESS #ifdef CONFIG_ESP32_WIFI_SAVE_PARAM