Roman Okhrimenko | dc0ca08 | 2023-06-21 20:49:51 +0300 | [diff] [blame] | 1 | /* |
| 2 | * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD |
| 3 | * |
| 4 | * SPDX-License-Identifier: Apache-2.0 |
| 5 | */ |
| 6 | |
| 7 | #include <stdbool.h> |
| 8 | #include <stdlib.h> |
| 9 | #include <string.h> |
| 10 | |
| 11 | #include <bootutil/bootutil.h> |
| 12 | #include <bootutil/bootutil_log.h> |
| 13 | |
| 14 | #include "sdkconfig.h" |
| 15 | #include "esp_err.h" |
| 16 | #include "bootloader_flash_priv.h" |
| 17 | #include "esp_flash_encrypt.h" |
| 18 | |
| 19 | #include "flash_map_backend/flash_map_backend.h" |
| 20 | #include "sysflash/sysflash.h" |
| 21 | |
| 22 | #ifndef ARRAY_SIZE |
| 23 | # define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) |
| 24 | #endif |
| 25 | |
| 26 | #ifndef MIN |
| 27 | # define MIN(a, b) (((a) < (b)) ? (a) : (b)) |
| 28 | #endif |
| 29 | |
| 30 | #ifndef ALIGN_UP |
| 31 | # define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) |
| 32 | #endif |
| 33 | |
| 34 | #ifndef ALIGN_DOWN |
| 35 | # define ALIGN_DOWN(num, align) ((num) & ~((align) - 1)) |
| 36 | #endif |
| 37 | |
| 38 | #ifndef ALIGN_OFFSET |
| 39 | # define ALIGN_OFFSET(num, align) ((num) & ((align) - 1)) |
| 40 | #endif |
| 41 | |
| 42 | #ifndef IS_ALIGNED |
| 43 | # define IS_ALIGNED(num, align) (ALIGN_OFFSET((num), (align)) == 0) |
| 44 | #endif |
| 45 | |
| 46 | #define FLASH_BUFFER_SIZE 256 /* SPI Flash block size */ |
| 47 | |
| 48 | _Static_assert(IS_ALIGNED(FLASH_BUFFER_SIZE, 4), "Buffer size for SPI Flash operations must be 4-byte aligned."); |
| 49 | |
| 50 | #define BOOTLOADER_START_ADDRESS CONFIG_BOOTLOADER_OFFSET_IN_FLASH |
| 51 | #define BOOTLOADER_SIZE CONFIG_ESP_BOOTLOADER_SIZE |
| 52 | #define APPLICATION_PRIMARY_START_ADDRESS CONFIG_ESP_APPLICATION_PRIMARY_START_ADDRESS |
| 53 | #define APPLICATION_SECONDARY_START_ADDRESS CONFIG_ESP_APPLICATION_SECONDARY_START_ADDRESS |
| 54 | #define APPLICATION_SIZE CONFIG_ESP_APPLICATION_SIZE |
| 55 | #define SCRATCH_OFFSET CONFIG_ESP_SCRATCH_OFFSET |
| 56 | #define SCRATCH_SIZE CONFIG_ESP_SCRATCH_SIZE |
| 57 | |
| 58 | extern int ets_printf(const char *fmt, ...); |
| 59 | |
| 60 | static const struct flash_area bootloader = { |
| 61 | .fa_id = FLASH_AREA_BOOTLOADER, |
| 62 | .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, |
| 63 | .fa_off = BOOTLOADER_START_ADDRESS, |
| 64 | .fa_size = BOOTLOADER_SIZE, |
| 65 | }; |
| 66 | |
| 67 | static const struct flash_area primary_img0 = { |
| 68 | .fa_id = FLASH_AREA_IMAGE_PRIMARY(0), |
| 69 | .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, |
| 70 | .fa_off = APPLICATION_PRIMARY_START_ADDRESS, |
| 71 | .fa_size = APPLICATION_SIZE, |
| 72 | }; |
| 73 | |
| 74 | static const struct flash_area secondary_img0 = { |
| 75 | .fa_id = FLASH_AREA_IMAGE_SECONDARY(0), |
| 76 | .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, |
| 77 | .fa_off = APPLICATION_SECONDARY_START_ADDRESS, |
| 78 | .fa_size = APPLICATION_SIZE, |
| 79 | }; |
| 80 | |
| 81 | static const struct flash_area scratch_img0 = { |
| 82 | .fa_id = FLASH_AREA_IMAGE_SCRATCH, |
| 83 | .fa_device_id = FLASH_DEVICE_INTERNAL_FLASH, |
| 84 | .fa_off = SCRATCH_OFFSET, |
| 85 | .fa_size = SCRATCH_SIZE, |
| 86 | }; |
| 87 | |
| 88 | static const struct flash_area *s_flash_areas[] = { |
| 89 | &bootloader, |
| 90 | &primary_img0, |
| 91 | &secondary_img0, |
| 92 | &scratch_img0, |
| 93 | }; |
| 94 | |
| 95 | static const struct flash_area *prv_lookup_flash_area(uint8_t id) { |
| 96 | for (size_t i = 0; i < ARRAY_SIZE(s_flash_areas); i++) { |
| 97 | const struct flash_area *area = s_flash_areas[i]; |
| 98 | if (id == area->fa_id) { |
| 99 | return area; |
| 100 | } |
| 101 | } |
| 102 | return NULL; |
| 103 | } |
| 104 | |
| 105 | int flash_area_open(uint8_t id, const struct flash_area **area_outp) |
| 106 | { |
| 107 | BOOT_LOG_DBG("%s: ID=%d", __func__, (int)id); |
| 108 | const struct flash_area *area = prv_lookup_flash_area(id); |
| 109 | *area_outp = area; |
| 110 | return area != NULL ? 0 : -1; |
| 111 | } |
| 112 | |
| 113 | void flash_area_close(const struct flash_area *area) |
| 114 | { |
| 115 | |
| 116 | } |
| 117 | |
| 118 | static bool aligned_flash_read(uintptr_t addr, void *dest, size_t size) |
| 119 | { |
| 120 | if (IS_ALIGNED(addr, 4) && IS_ALIGNED((uintptr_t)dest, 4) && IS_ALIGNED(size, 4)) { |
| 121 | /* A single read operation is enough when when all parameters are aligned */ |
| 122 | |
| 123 | return bootloader_flash_read(addr, dest, size, true) == ESP_OK; |
| 124 | } |
| 125 | |
| 126 | const uint32_t aligned_addr = ALIGN_DOWN(addr, 4); |
| 127 | const uint32_t addr_offset = ALIGN_OFFSET(addr, 4); |
| 128 | uint32_t bytes_remaining = size; |
| 129 | uint8_t read_data[FLASH_BUFFER_SIZE] = {0}; |
| 130 | |
| 131 | /* Align the read address to 4-byte boundary and ensure read size is a multiple of 4 bytes */ |
| 132 | |
| 133 | uint32_t bytes = MIN(bytes_remaining + addr_offset, sizeof(read_data)); |
| 134 | if (bootloader_flash_read(aligned_addr, read_data, ALIGN_UP(bytes, 4), true) != ESP_OK) { |
| 135 | return false; |
| 136 | } |
| 137 | |
| 138 | /* Skip non-useful data which may have been read for adjusting the alignment */ |
| 139 | |
| 140 | uint32_t bytes_read = bytes - addr_offset; |
| 141 | memcpy(dest, &read_data[addr_offset], bytes_read); |
| 142 | |
| 143 | bytes_remaining -= bytes_read; |
| 144 | |
| 145 | /* Read remaining data from Flash in case requested size is greater than buffer size */ |
| 146 | |
| 147 | uint32_t offset = bytes; |
| 148 | |
| 149 | while (bytes_remaining != 0) { |
| 150 | bytes = MIN(bytes_remaining, sizeof(read_data)); |
| 151 | if (bootloader_flash_read(aligned_addr + offset, read_data, ALIGN_UP(bytes, 4), true) != ESP_OK) { |
| 152 | return false; |
| 153 | } |
| 154 | |
| 155 | memcpy(&((uint8_t *)dest)[bytes_read], read_data, bytes); |
| 156 | |
| 157 | offset += bytes; |
| 158 | bytes_read += bytes; |
| 159 | bytes_remaining -= bytes; |
| 160 | } |
| 161 | |
| 162 | return true; |
| 163 | } |
| 164 | |
| 165 | int flash_area_read(const struct flash_area *fa, uint32_t off, void *dst, |
| 166 | uint32_t len) |
| 167 | { |
| 168 | if (fa->fa_device_id != FLASH_DEVICE_INTERNAL_FLASH) { |
| 169 | return -1; |
| 170 | } |
| 171 | |
| 172 | const uint32_t end_offset = off + len; |
| 173 | if (end_offset > fa->fa_size) { |
| 174 | BOOT_LOG_ERR("%s: Out of Bounds (0x%x vs 0x%x)", __func__, end_offset, fa->fa_size); |
| 175 | return -1; |
| 176 | } |
| 177 | |
| 178 | bool success = aligned_flash_read(fa->fa_off + off, dst, len); |
| 179 | if (!success) { |
| 180 | BOOT_LOG_ERR("%s: Flash read failed", __func__); |
| 181 | |
| 182 | return -1; |
| 183 | } |
| 184 | |
| 185 | return 0; |
| 186 | } |
| 187 | |
| 188 | int flash_area_write(const struct flash_area *fa, uint32_t off, const void *src, |
| 189 | uint32_t len) |
| 190 | { |
| 191 | if (fa->fa_device_id != FLASH_DEVICE_INTERNAL_FLASH) { |
| 192 | return -1; |
| 193 | } |
| 194 | |
| 195 | const uint32_t end_offset = off + len; |
| 196 | if (end_offset > fa->fa_size) { |
| 197 | BOOT_LOG_ERR("%s: Out of Bounds (0x%x vs 0x%x)", __func__, end_offset, fa->fa_size); |
| 198 | return -1; |
| 199 | } |
| 200 | |
| 201 | bool flash_encryption_enabled = esp_flash_encryption_enabled(); |
| 202 | |
| 203 | const uint32_t start_addr = fa->fa_off + off; |
| 204 | BOOT_LOG_DBG("%s: Addr: 0x%08x Length: %d", __func__, (int)start_addr, (int)len); |
| 205 | |
| 206 | if (bootloader_flash_write(start_addr, (void *)src, len, flash_encryption_enabled) != ESP_OK) { |
| 207 | BOOT_LOG_ERR("%s: Flash write failed", __func__); |
| 208 | return -1; |
| 209 | } |
| 210 | |
| 211 | return 0; |
| 212 | } |
| 213 | |
| 214 | int flash_area_erase(const struct flash_area *fa, uint32_t off, uint32_t len) |
| 215 | { |
| 216 | if (fa->fa_device_id != FLASH_DEVICE_INTERNAL_FLASH) { |
| 217 | return -1; |
| 218 | } |
| 219 | |
| 220 | if ((len % FLASH_SECTOR_SIZE) != 0 || (off % FLASH_SECTOR_SIZE) != 0) { |
| 221 | BOOT_LOG_ERR("%s: Not aligned on sector Offset: 0x%x Length: 0x%x", |
| 222 | __func__, (int)off, (int)len); |
| 223 | return -1; |
| 224 | } |
| 225 | |
| 226 | const uint32_t start_addr = fa->fa_off + off; |
| 227 | BOOT_LOG_DBG("%s: Addr: 0x%08x Length: %d", __func__, (int)start_addr, (int)len); |
| 228 | |
| 229 | if (bootloader_flash_erase_range(start_addr, len) != ESP_OK) { |
| 230 | BOOT_LOG_ERR("%s: Flash erase failed", __func__); |
| 231 | return -1; |
| 232 | } |
| 233 | #if VALIDATE_PROGRAM_OP |
| 234 | for (size_t i = 0; i < len; i++) { |
| 235 | uint8_t *val = (void *)(start_addr + i); |
| 236 | if (*val != 0xff) { |
| 237 | BOOT_LOG_ERR("%s: Erase at 0x%x Failed", __func__, (int)val); |
| 238 | assert(0); |
| 239 | } |
| 240 | } |
| 241 | #endif |
| 242 | |
| 243 | return 0; |
| 244 | } |
| 245 | |
| 246 | uint32_t flash_area_align(const struct flash_area *area) |
| 247 | { |
| 248 | static size_t align = 0; |
| 249 | |
| 250 | if (align == 0) { |
| 251 | bool flash_encryption_enabled = esp_flash_encryption_enabled(); |
| 252 | |
| 253 | if (flash_encryption_enabled) { |
| 254 | align = 32; |
| 255 | } else { |
| 256 | align = 4; |
| 257 | } |
| 258 | } |
| 259 | return align; |
| 260 | } |
| 261 | |
| 262 | uint8_t flash_area_erased_val(const struct flash_area *area) |
| 263 | { |
| 264 | return 0xff; |
| 265 | } |
| 266 | |
| 267 | int flash_area_get_sectors(int fa_id, uint32_t *count, |
| 268 | struct flash_sector *sectors) |
| 269 | { |
| 270 | const struct flash_area *fa = prv_lookup_flash_area(fa_id); |
| 271 | if (fa->fa_device_id != FLASH_DEVICE_INTERNAL_FLASH) { |
| 272 | return -1; |
| 273 | } |
| 274 | |
| 275 | const size_t sector_size = FLASH_SECTOR_SIZE; |
| 276 | uint32_t total_count = 0; |
| 277 | for (size_t off = 0; off < fa->fa_size; off += sector_size) { |
| 278 | // Note: Offset here is relative to flash area, not device |
| 279 | sectors[total_count].fs_off = off; |
| 280 | sectors[total_count].fs_size = sector_size; |
| 281 | total_count++; |
| 282 | } |
| 283 | |
| 284 | *count = total_count; |
| 285 | return 0; |
| 286 | } |
| 287 | |
| 288 | int flash_area_id_from_multi_image_slot(int image_index, int slot) |
| 289 | { |
| 290 | BOOT_LOG_DBG("%s", __func__); |
| 291 | switch (slot) { |
| 292 | case 0: |
| 293 | return FLASH_AREA_IMAGE_PRIMARY(image_index); |
| 294 | case 1: |
| 295 | return FLASH_AREA_IMAGE_SECONDARY(image_index); |
| 296 | } |
| 297 | |
| 298 | BOOT_LOG_ERR("Unexpected Request: image_index=%d, slot=%d", image_index, slot); |
| 299 | return -1; /* flash_area_open will fail on that */ |
| 300 | } |
| 301 | |
| 302 | int flash_area_id_from_image_slot(int slot) |
| 303 | { |
| 304 | return flash_area_id_from_multi_image_slot(0, slot); |
| 305 | } |
| 306 | |
| 307 | int flash_area_to_sectors(int idx, int *cnt, struct flash_area *fa) |
| 308 | { |
| 309 | return -1; |
| 310 | } |
| 311 | |
| 312 | void mcuboot_assert_handler(const char *file, int line, const char *func) |
| 313 | { |
| 314 | ets_printf("assertion failed: file \"%s\", line %d, func: %s\n", file, line, func); |
| 315 | abort(); |
| 316 | } |