|
| 1 | +/* |
| 2 | + * Copyright (c) 2021 Particle Industries, Inc. All rights reserved. |
| 3 | + * |
| 4 | + * This library is free software; you can redistribute it and/or |
| 5 | + * modify it under the terms of the GNU Lesser General Public |
| 6 | + * License as published by the Free Software Foundation, either |
| 7 | + * version 3 of the License, or (at your option) any later version. |
| 8 | + * |
| 9 | + * This library is distributed in the hope that it will be useful, |
| 10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | + * Lesser General Public License for more details. |
| 13 | + * |
| 14 | + * You should have received a copy of the GNU Lesser General Public |
| 15 | + * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
| 16 | + */ |
| 17 | + |
| 18 | +#include "stdbool.h" |
| 19 | +#include "rtl8721d.h" |
| 20 | +#include "check.h" |
| 21 | +#include "flash_hal.h" |
| 22 | +#include "boot_info.h" |
| 23 | +#include "flash_mal.h" |
| 24 | +#include "flash_common.h" |
| 25 | + |
| 26 | +extern FLASH_InitTypeDef flash_init_para; |
| 27 | + |
| 28 | +extern uintptr_t link_system_part1_flash_start; |
| 29 | +extern uintptr_t link_user_part_flash_end; |
| 30 | +extern uintptr_t link_km0_mbr_flash_end; |
| 31 | +extern uintptr_t link_platform_flash_end; |
| 32 | +extern uintptr_t link_part1_module_info_flash_start; |
| 33 | + |
| 34 | +static void flash_init(void) { |
| 35 | + RCC_PeriphClockCmd(APBPeriph_FLASH, APBPeriph_FLASH_CLOCK_XTAL, ENABLE); |
| 36 | + |
| 37 | + uint32_t Temp = HAL_READ32(SYSTEM_CTRL_BASE_LP, REG_LP_CLK_CTRL0); |
| 38 | + Temp &= ~(BIT_MASK_FLASH_CLK_SEL << BIT_SHIFT_FLASH_CLK_SEL); |
| 39 | + Temp |= BIT_SHIFT_FLASH_CLK_XTAL; |
| 40 | + HAL_WRITE32(SYSTEM_CTRL_BASE_LP, REG_LP_CLK_CTRL0, Temp); |
| 41 | + |
| 42 | + FLASH_StructInit(&flash_init_para); |
| 43 | + FLASH_Init(SpicOneBitMode); |
| 44 | + |
| 45 | + uint8_t flashId[3]; |
| 46 | + FLASH_RxCmd(flash_init_para.FLASH_cmd_rd_id, 3, flashId); |
| 47 | + if (flashId[0] == 0x20) { |
| 48 | + flash_init_para.FLASH_cmd_chip_e = 0xC7; |
| 49 | + } |
| 50 | +} |
| 51 | + |
| 52 | +static bool flash_copy(uintptr_t src_addr, uintptr_t dest_addr, size_t size) { |
| 53 | + const uint32_t COPY_BLOCK_SIZE = 256; |
| 54 | + uint8_t buf[COPY_BLOCK_SIZE]; |
| 55 | + const uintptr_t src_end_addr = src_addr + size; |
| 56 | + |
| 57 | + uint32_t sectorNum = (size / 4096) + (((size % 4096) > 0) ? 1 : 0); |
| 58 | + if (hal_flash_erase_sector(dest_addr, sectorNum) != 0) { |
| 59 | + return false; |
| 60 | + } |
| 61 | + |
| 62 | + while (src_addr < src_end_addr) { |
| 63 | + size_t n = src_end_addr - src_addr; |
| 64 | + if (n > sizeof(buf)) { |
| 65 | + n = sizeof(buf); |
| 66 | + } |
| 67 | + if (hal_flash_read(src_addr, buf, n) != 0) { |
| 68 | + return false; |
| 69 | + } |
| 70 | + if (hal_flash_write(dest_addr, buf, n) != 0) { |
| 71 | + return false; |
| 72 | + } |
| 73 | + if (memcmp((uint8_t*)src_addr, (uint8_t*)dest_addr, n)) { |
| 74 | + return false; |
| 75 | + } |
| 76 | + src_addr += n; |
| 77 | + dest_addr += n; |
| 78 | + } |
| 79 | + return true; |
| 80 | +} |
| 81 | + |
| 82 | +bool bootloaderUpdateIfPending(void) { |
| 83 | + flash_init(); |
| 84 | + |
| 85 | + flash_update_info_t info = {}; |
| 86 | + |
| 87 | + uint32_t infoAddr = BOOT_INFO_FLASH_XIP_START_ADDR + KM0_BOOTLOADER_UPDATE_INFO_OFFSET; |
| 88 | + memcpy(&info, (void*)infoAddr, sizeof(info)); |
| 89 | + |
| 90 | + // Validate flash_update_info_t integrity |
| 91 | + if (info.magic_num != KM0_BOOTLOADER_UPDATE_MAGIC_NUMBER |
| 92 | + || Compute_CRC32((const uint8_t*)&info, sizeof(flash_update_info_t) - sizeof(info.crc32), NULL) != info.crc32) { |
| 93 | + goto invalidate; |
| 94 | + } |
| 95 | + |
| 96 | + // flash_update_info_t looks valid |
| 97 | + // Validate source location, source should be within a potential dynamic OTA location region |
| 98 | + const uintptr_t ota_region_start = (uintptr_t)&link_system_part1_flash_start; |
| 99 | + const uintptr_t ota_region_end = (uintptr_t)&link_user_part_flash_end; |
| 100 | + if (!(info.src_addr >= ota_region_start && info.src_addr < ota_region_end |
| 101 | + && info.size > 0 && info.src_addr + info.size <= ota_region_end)) { |
| 102 | + goto invalidate; |
| 103 | + } |
| 104 | + |
| 105 | + // Check destination location, it should be within flash excluding MBR |
| 106 | + if (!(info.dest_addr >= (uintptr_t)&link_km0_mbr_flash_end && info.dest_addr < (uintptr_t)&link_platform_flash_end |
| 107 | + && info.dest_addr + info.size < (uintptr_t)&link_platform_flash_end)) { |
| 108 | + goto invalidate; |
| 109 | + } |
| 110 | + |
| 111 | + // Destination-specific checks. |
| 112 | + // When updating KM0 part1, if the image is not encrypted and we have encryption enabled, then invalidate the update. |
| 113 | + // IE dont allow unencrypted updates once we enable encryption. |
| 114 | + uint8_t userEfuse0 = 0xFF; |
| 115 | + EFUSE_PMAP_READ8(0, USER_KEY_0_EFUSE_ADDRESS, &userEfuse0, L25EOUTVOLTAGE); |
| 116 | + bool part1_encryption_enabled = !(userEfuse0 & PART1_ENCRYPTED_BIT); |
| 117 | + bool pending_image_encrypted = (info.flags & MODULE_ENCRYPTED); |
| 118 | + if (info.dest_addr == (uintptr_t)&link_part1_module_info_flash_start && (!pending_image_encrypted && part1_encryption_enabled)) { |
| 119 | + goto invalidate; |
| 120 | + } |
| 121 | + |
| 122 | + bool enableRsip = false; |
| 123 | + if ((HAL_READ32(SYSTEM_CTRL_BASE_LP, REG_SYS_EFUSE_SYSCFG3) & BIT_SYS_FLASH_ENCRYPT_EN) != 0) { |
| 124 | + // Temporarily disable RSIP for memory copying, should leave the image in the OTA region as-is |
| 125 | + uint32_t km0_system_control = HAL_READ32(SYSTEM_CTRL_BASE_LP, REG_LP_KM0_CTRL); |
| 126 | + HAL_WRITE32(SYSTEM_CTRL_BASE_LP, REG_LP_KM0_CTRL, (km0_system_control & (~BIT_LSYS_PLFM_FLASH_SCE))); |
| 127 | + enableRsip = true; |
| 128 | + } |
| 129 | + |
| 130 | + bool ret = flash_copy(info.src_addr, info.dest_addr, info.size); |
| 131 | + if (enableRsip) { |
| 132 | + uint32_t km0_system_control = HAL_READ32(SYSTEM_CTRL_BASE_LP, REG_LP_KM0_CTRL); |
| 133 | + HAL_WRITE32(SYSTEM_CTRL_BASE_LP, REG_LP_KM0_CTRL, (km0_system_control | BIT_LSYS_PLFM_FLASH_SCE)); |
| 134 | + } |
| 135 | + |
| 136 | + if (!ret) { |
| 137 | + // Flash copy failed. Try rebooting |
| 138 | + return false; |
| 139 | + } |
| 140 | + |
| 141 | + // Flash copy succeeded. If this is an encrypted part1 image, and encryption isnt enabled, enable it in pysical efuse |
| 142 | + if (info.dest_addr == (uintptr_t)&link_part1_module_info_flash_start && pending_image_encrypted && !part1_encryption_enabled) { |
| 143 | + userEfuse0 &= ~(PART1_ENCRYPTED_BIT); |
| 144 | + bool success = (EFUSE_PMAP_WRITE8(0, USER_KEY_0_EFUSE_ADDRESS, userEfuse0, L25EOUTVOLTAGE) != 0); |
| 145 | + if (!success) { |
| 146 | + return false; |
| 147 | + } |
| 148 | + } |
| 149 | + |
| 150 | +invalidate: |
| 151 | + memset(&info, 0x00, sizeof(info)); |
| 152 | + if (hal_flash_write(infoAddr, (const uint8_t*)&info, sizeof(info)) != 0) { |
| 153 | + hal_flash_erase_sector(BOOT_INFO_FLASH_XIP_START_ADDR, 1); |
| 154 | + } |
| 155 | + |
| 156 | + return true; |
| 157 | +} |
0 commit comments