Mike Frysinger | 71b2ef7 | 2022-09-12 18:54:36 | [diff] [blame] | 1 | /* Copyright 2018 The ChromiumOS Authors |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 2 | * Use of this source code is governed by a BSD-style license that can be |
| 3 | * found in the LICENSE file. |
| 4 | */ |
| 5 | /* Flash memory module for STM32H7 family */ |
| 6 | |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 7 | #include "clock.h" |
Jeremy Bettis | 88684b2 | 2022-11-23 22:28:01 | [diff] [blame] | 8 | #include "common.h" |
Nicolas Boichat | 63f9c77 | 2018-07-12 07:51:45 | [diff] [blame] | 9 | #include "cpu.h" |
Josie Nordrum | 0574818 | 2020-12-06 19:32:14 | [diff] [blame] | 10 | #include "flash-regs.h" |
Jeremy Bettis | 88684b2 | 2022-11-23 22:28:01 | [diff] [blame] | 11 | #include "flash.h" |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 12 | #include "hooks.h" |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 13 | #include "panic.h" |
Jeremy Bettis | 88684b2 | 2022-11-23 22:28:01 | [diff] [blame] | 14 | #include "registers.h" |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 15 | #include "system.h" |
| 16 | #include "task.h" |
| 17 | #include "timer.h" |
| 18 | #include "util.h" |
| 19 | #include "watchdog.h" |
| 20 | |
| 21 | /* |
| 22 | * Approximate number of CPU cycles per iteration of the loop when polling |
| 23 | * the flash status |
| 24 | */ |
| 25 | #define CYCLE_PER_FLASH_LOOP 2 |
| 26 | |
| 27 | /* Flash 256-bit word programming timeout. */ |
| 28 | #define FLASH_TIMEOUT_US 600 |
| 29 | |
| 30 | /* |
| 31 | * Flash 128-KB block erase timeout. |
| 32 | * Datasheet says maximum is about 4 seconds in x8. |
| 33 | * Real delay seems to be: < 1 second in x64, < 2 seconds in x8. |
| 34 | */ |
| 35 | #define FLASH_ERASE_TIMEOUT_US (4200 * MSEC) |
| 36 | |
| 37 | /* |
| 38 | * Option bytes programming timeout. |
| 39 | * No specification, real delay seems to be around 300ms. |
| 40 | */ |
| 41 | #define FLASH_OPT_PRG_TIMEOUT_US (1000 * MSEC) |
| 42 | |
| 43 | /* |
| 44 | * All variants have 2 banks (as in parallel hardware / controllers) |
| 45 | * not what is called 'bank' in the common code (ie Write-Protect sectors) |
| 46 | * both have the same number of 128KB blocks. |
| 47 | */ |
Jack Rosenthal | 195d11b | 2022-06-27 20:28:58 | [diff] [blame] | 48 | #define HWBANK_SIZE (CONFIG_FLASH_SIZE_BYTES / 2) |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 49 | #define BLOCKS_PER_HWBANK (HWBANK_SIZE / CONFIG_FLASH_ERASE_SIZE) |
Gwendal Grignou | ac77140 | 2019-03-11 23:07:55 | [diff] [blame] | 50 | #define BLOCKS_HWBANK_MASK (BIT(BLOCKS_PER_HWBANK) - 1) |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 51 | |
| 52 | /* |
| 53 | * We can tune the power consumption vs erase/write speed |
| 54 | * by default, go fast (and consume current) |
| 55 | */ |
| 56 | #define DEFAULT_PSIZE FLASH_CR_PSIZE_DWORD |
| 57 | |
| 58 | /* Can no longer write/erase flash until next reboot */ |
| 59 | static int access_disabled; |
| 60 | /* Can no longer modify write-protection in option bytes until next reboot */ |
| 61 | static int option_disabled; |
| 62 | /* Is physical flash stuck protected? (avoid reboot loop) */ |
| 63 | static int stuck_locked; |
| 64 | |
Craig Hesling | 58bd6e3 | 2020-01-07 20:52:30 | [diff] [blame] | 65 | #define FLASH_SYSJUMP_TAG 0x5750 /* "WP" - Write Protect */ |
| 66 | #define FLASH_HOOK_VERSION 1 |
| 67 | |
| 68 | /* The previous write protect state before sys jump */ |
| 69 | struct flash_wp_state { |
| 70 | int access_disabled; |
| 71 | int option_disabled; |
| 72 | int stuck_locked; |
| 73 | }; |
| 74 | |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 75 | static inline int calculate_flash_timeout(void) |
| 76 | { |
Jack Rosenthal | 195d11b | 2022-06-27 20:28:58 | [diff] [blame] | 77 | return (FLASH_TIMEOUT_US * (clock_get_freq() / SECOND) / |
| 78 | CYCLE_PER_FLASH_LOOP); |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 79 | } |
| 80 | |
| 81 | static int unlock(int bank) |
| 82 | { |
| 83 | /* unlock CR only if needed */ |
| 84 | if (STM32_FLASH_CR(bank) & FLASH_CR_LOCK) { |
| 85 | /* |
| 86 | * We may have already locked the flash module and get a bus |
| 87 | * fault in the attempt to unlock. Need to disable bus fault |
| 88 | * handler now. |
| 89 | */ |
| 90 | ignore_bus_fault(1); |
| 91 | |
| 92 | STM32_FLASH_KEYR(bank) = FLASH_KEYR_KEY1; |
| 93 | STM32_FLASH_KEYR(bank) = FLASH_KEYR_KEY2; |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 94 | ignore_bus_fault(0); |
| 95 | } |
| 96 | |
Jack Rosenthal | 195d11b | 2022-06-27 20:28:58 | [diff] [blame] | 97 | return (STM32_FLASH_CR(bank) & FLASH_CR_LOCK) ? EC_ERROR_UNKNOWN : |
| 98 | EC_SUCCESS; |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 99 | } |
| 100 | |
| 101 | static void lock(int bank) |
| 102 | { |
| 103 | STM32_FLASH_CR(bank) |= FLASH_CR_LOCK; |
| 104 | } |
| 105 | |
| 106 | static int unlock_optb(void) |
| 107 | { |
| 108 | if (option_disabled) |
| 109 | return EC_ERROR_ACCESS_DENIED; |
| 110 | |
| 111 | if (unlock(0)) |
| 112 | return EC_ERROR_UNKNOWN; |
| 113 | |
Josie Nordrum | 0574818 | 2020-12-06 19:32:14 | [diff] [blame] | 114 | if (flash_option_bytes_locked()) { |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 115 | /* |
| 116 | * We may have already locked the flash module and get a bus |
| 117 | * fault in the attempt to unlock. Need to disable bus fault |
| 118 | * handler now. |
| 119 | */ |
| 120 | ignore_bus_fault(1); |
| 121 | |
Josie Nordrum | 0574818 | 2020-12-06 19:32:14 | [diff] [blame] | 122 | unlock_flash_option_bytes(); |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 123 | ignore_bus_fault(0); |
| 124 | } |
| 125 | |
Jack Rosenthal | 195d11b | 2022-06-27 20:28:58 | [diff] [blame] | 126 | return flash_option_bytes_locked() ? EC_ERROR_UNKNOWN : EC_SUCCESS; |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 127 | } |
| 128 | |
| 129 | static int commit_optb(void) |
| 130 | { |
| 131 | /* might use this before timer_init, cannot use get_time/usleep */ |
Jack Rosenthal | 195d11b | 2022-06-27 20:28:58 | [diff] [blame] | 132 | int timeout = (FLASH_OPT_PRG_TIMEOUT_US * (clock_get_freq() / SECOND) / |
| 133 | CYCLE_PER_FLASH_LOOP); |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 134 | |
| 135 | STM32_FLASH_OPTCR(0) |= FLASH_OPTCR_OPTSTART; |
| 136 | |
| 137 | while (STM32_FLASH_OPTSR_CUR(0) & FLASH_OPTSR_BUSY && timeout-- > 0) |
| 138 | ; |
| 139 | |
Josie Nordrum | 0574818 | 2020-12-06 19:32:14 | [diff] [blame] | 140 | lock_flash_option_bytes(); |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 141 | lock(0); |
| 142 | |
| 143 | return (timeout > 0) ? EC_SUCCESS : EC_ERROR_TIMEOUT; |
| 144 | } |
| 145 | |
| 146 | static void protect_blocks(uint32_t blocks) |
| 147 | { |
| 148 | if (unlock_optb()) |
| 149 | return; |
Nicolas Boichat | b69b099 | 2018-07-06 04:53:48 | [diff] [blame] | 150 | STM32_FLASH_WPSN_PRG(0) &= ~(blocks & BLOCKS_HWBANK_MASK); |
Jack Rosenthal | 195d11b | 2022-06-27 20:28:58 | [diff] [blame] | 151 | STM32_FLASH_WPSN_PRG(1) &= |
| 152 | ~((blocks >> BLOCKS_PER_HWBANK) & BLOCKS_HWBANK_MASK); |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 153 | commit_optb(); |
| 154 | } |
| 155 | |
Josie Nordrum | 0574818 | 2020-12-06 19:32:14 | [diff] [blame] | 156 | /* |
| 157 | * Helper function definitions for consistency with F4 to enable flash |
| 158 | * physical unitesting |
| 159 | */ |
| 160 | void unlock_flash_control_register(void) |
| 161 | { |
| 162 | unlock(0); |
| 163 | unlock(1); |
| 164 | } |
| 165 | |
| 166 | void unlock_flash_option_bytes(void) |
| 167 | { |
| 168 | /* |
| 169 | * Always use bank 0 flash controller as there is only one option bytes |
| 170 | * set for both banks. See http://b/181130245 |
| 171 | * |
| 172 | * Consecutively program values. Ref: RM0433:4.9.2 |
| 173 | */ |
| 174 | STM32_FLASH_OPTKEYR(0) = FLASH_OPTKEYR_KEY1; |
| 175 | STM32_FLASH_OPTKEYR(0) = FLASH_OPTKEYR_KEY2; |
| 176 | } |
| 177 | |
| 178 | void disable_flash_option_bytes(void) |
| 179 | { |
| 180 | ignore_bus_fault(1); |
| 181 | /* |
| 182 | * Always use bank 0 flash controller as there is only one option bytes |
| 183 | * set for both banks. See http://b/181130245 |
| 184 | * |
| 185 | * Writing anything other than the pre-defined keys to the option key |
| 186 | * register results in a bus fault and the register being locked until |
| 187 | * reboot (even with a further correct key write). |
| 188 | */ |
| 189 | STM32_FLASH_OPTKEYR(0) = 0xffffffff; |
| 190 | ignore_bus_fault(0); |
| 191 | } |
| 192 | |
| 193 | void disable_flash_control_register(void) |
| 194 | { |
| 195 | ignore_bus_fault(1); |
| 196 | /* |
| 197 | * Writing anything other than the pre-defined keys to a key |
| 198 | * register results in a bus fault and the register being locked until |
| 199 | * reboot (even with a further correct key write). |
| 200 | */ |
| 201 | STM32_FLASH_KEYR(0) = 0xffffffff; |
| 202 | STM32_FLASH_KEYR(1) = 0xffffffff; |
| 203 | ignore_bus_fault(0); |
| 204 | } |
| 205 | |
| 206 | void lock_flash_control_register(void) |
| 207 | { |
| 208 | lock(0); |
| 209 | lock(1); |
| 210 | } |
| 211 | |
| 212 | void lock_flash_option_bytes(void) |
| 213 | { |
| 214 | /* |
| 215 | * Always use bank 0 flash controller as there is only one option bytes |
| 216 | * set for both banks. See http://b/181130245 |
| 217 | */ |
| 218 | STM32_FLASH_OPTCR(0) |= FLASH_OPTCR_OPTLOCK; |
| 219 | } |
| 220 | |
| 221 | bool flash_option_bytes_locked(void) |
| 222 | { |
| 223 | /* |
| 224 | * Always use bank 0 flash controller as there is only one option bytes |
| 225 | * set for both banks. See http://b/181130245 |
| 226 | */ |
Jack Rosenthal | 195d11b | 2022-06-27 20:28:58 | [diff] [blame] | 227 | return !!(STM32_FLASH_OPTCR(0) & FLASH_OPTCR_OPTLOCK); |
Josie Nordrum | 0574818 | 2020-12-06 19:32:14 | [diff] [blame] | 228 | } |
| 229 | |
| 230 | bool flash_control_register_locked(void) |
| 231 | { |
| 232 | return !!(STM32_FLASH_CR(0) & FLASH_CR_LOCK) && |
| 233 | !!(STM32_FLASH_CR(1) & FLASH_CR_LOCK); |
| 234 | } |
| 235 | |
Nicolas Boichat | c98ea9a | 2018-09-12 21:39:39 | [diff] [blame] | 236 | /* |
| 237 | * If RDP as PSTATE option is defined, use that as 'Write Protect enabled' flag: |
| 238 | * it makes no sense to be able to unlock RO, as that'd allow flashing |
| 239 | * arbitrary RO that could read back all flash. |
| 240 | * |
| 241 | * crbug.com/888109: Do not copy this code over to other STM32 chips without |
| 242 | * understanding the full implications. |
| 243 | * |
| 244 | * If RDP is not defined, use the option bytes RSS1 bit. |
| 245 | * TODO(crbug.com/888104): Validate that using RSS1 for this purpose is safe. |
| 246 | */ |
Tom Hughes | 220d0bf | 2019-07-09 21:19:20 | [diff] [blame] | 247 | #ifndef CONFIG_FLASH_READOUT_PROTECTION_AS_PSTATE |
| 248 | #error "crbug.com/888104: Using RSS1 for write protect PSTATE may not be safe." |
| 249 | #endif |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 250 | static int is_wp_enabled(void) |
| 251 | { |
Nicolas Boichat | c98ea9a | 2018-09-12 21:39:39 | [diff] [blame] | 252 | #ifdef CONFIG_FLASH_READOUT_PROTECTION_AS_PSTATE |
Jack Rosenthal | 195d11b | 2022-06-27 20:28:58 | [diff] [blame] | 253 | return (STM32_FLASH_OPTSR_CUR(0) & FLASH_OPTSR_RDP_MASK) != |
| 254 | FLASH_OPTSR_RDP_LEVEL_0; |
Nicolas Boichat | c98ea9a | 2018-09-12 21:39:39 | [diff] [blame] | 255 | #else |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 256 | return !!(STM32_FLASH_OPTSR_CUR(0) & FLASH_OPTSR_RSS1); |
Nicolas Boichat | c98ea9a | 2018-09-12 21:39:39 | [diff] [blame] | 257 | #endif |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 258 | } |
| 259 | |
| 260 | static int set_wp(int enabled) |
| 261 | { |
| 262 | int rv; |
| 263 | |
| 264 | rv = unlock_optb(); |
| 265 | if (rv) |
| 266 | return rv; |
Nicolas Boichat | c98ea9a | 2018-09-12 21:39:39 | [diff] [blame] | 267 | |
| 268 | #ifdef CONFIG_FLASH_READOUT_PROTECTION_AS_PSTATE |
| 269 | if (enabled) { |
| 270 | /* Enable RDP level 1. */ |
| 271 | STM32_FLASH_OPTSR_PRG(0) = |
| 272 | (STM32_FLASH_OPTSR_PRG(0) & ~FLASH_OPTSR_RDP_MASK) | |
| 273 | FLASH_OPTSR_RDP_LEVEL_1; |
| 274 | } |
| 275 | #else |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 276 | if (enabled) |
| 277 | STM32_FLASH_OPTSR_PRG(0) |= FLASH_OPTSR_RSS1; |
| 278 | else |
| 279 | STM32_FLASH_OPTSR_PRG(0) &= ~FLASH_OPTSR_RSS1; |
Nicolas Boichat | c98ea9a | 2018-09-12 21:39:39 | [diff] [blame] | 280 | #endif |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 281 | |
| 282 | return commit_optb(); |
| 283 | } |
| 284 | |
| 285 | /*****************************************************************************/ |
| 286 | /* Physical layer APIs */ |
| 287 | |
Tim Lin | 670bd7e | 2021-06-04 06:11:25 | [diff] [blame] | 288 | int crec_flash_physical_write(int offset, int size, const char *data) |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 289 | { |
| 290 | int res = EC_SUCCESS; |
| 291 | int bank = offset / HWBANK_SIZE; |
| 292 | uint32_t *address = (void *)(CONFIG_PROGRAM_MEMORY_BASE + offset); |
| 293 | int timeout = calculate_flash_timeout(); |
| 294 | int i; |
| 295 | int unaligned = (uint32_t)data & (CONFIG_FLASH_WRITE_SIZE - 1); |
| 296 | uint32_t *data32 = (void *)data; |
| 297 | |
| 298 | if (access_disabled) |
| 299 | return EC_ERROR_ACCESS_DENIED; |
| 300 | |
| 301 | /* work on a single hardware bank at a time */ |
Nicolas Boichat | 71fa029 | 2018-07-06 06:30:17 | [diff] [blame] | 302 | if ((offset + size - 1) / HWBANK_SIZE != bank) |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 303 | return EC_ERROR_INVAL; |
| 304 | |
| 305 | if (unlock(bank) != EC_SUCCESS) |
| 306 | return EC_ERROR_UNKNOWN; |
| 307 | |
| 308 | /* Clear previous error status */ |
| 309 | STM32_FLASH_CCR(bank) = FLASH_CCR_ERR_MASK; |
| 310 | |
| 311 | /* select write parallelism */ |
Jack Rosenthal | 195d11b | 2022-06-27 20:28:58 | [diff] [blame] | 312 | STM32_FLASH_CR(bank) = (STM32_FLASH_CR(bank) & ~FLASH_CR_PSIZE_MASK) | |
| 313 | DEFAULT_PSIZE; |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 314 | |
| 315 | /* set PG bit */ |
| 316 | STM32_FLASH_CR(bank) |= FLASH_CR_PG; |
| 317 | |
| 318 | for (; size > 0; size -= CONFIG_FLASH_WRITE_SIZE) { |
| 319 | /* |
| 320 | * Reload the watchdog timer to avoid watchdog reset when doing |
| 321 | * long writing. |
| 322 | */ |
| 323 | watchdog_reload(); |
| 324 | |
| 325 | /* write a 256-bit flash word */ |
| 326 | if (unaligned) { |
Jack Rosenthal | 195d11b | 2022-06-27 20:28:58 | [diff] [blame] | 327 | for (i = 0; i < CONFIG_FLASH_WRITE_SIZE / 4; |
| 328 | i++, data += 4) |
| 329 | *address++ = (uint32_t)data[0] | |
| 330 | (data[1] << 8) | (data[2] << 16) | |
| 331 | (data[3] << 24); |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 332 | } else { |
| 333 | for (i = 0; i < CONFIG_FLASH_WRITE_SIZE / 4; i++) |
| 334 | *address++ = *data32++; |
| 335 | } |
| 336 | |
| 337 | /* Wait for writes to complete */ |
Jack Rosenthal | 195d11b | 2022-06-27 20:28:58 | [diff] [blame] | 338 | for (i = 0; |
| 339 | (STM32_FLASH_SR(bank) & (FLASH_SR_WBNE | FLASH_SR_QW)) && |
| 340 | (i < timeout); |
| 341 | i++) |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 342 | ; |
| 343 | |
| 344 | if (STM32_FLASH_SR(bank) & (FLASH_SR_WBNE | FLASH_SR_QW)) { |
| 345 | res = EC_ERROR_TIMEOUT; |
| 346 | goto exit_wr; |
| 347 | } |
| 348 | |
| 349 | if (STM32_FLASH_SR(bank) & FLASH_CCR_ERR_MASK) { |
| 350 | res = EC_ERROR_UNKNOWN; |
| 351 | goto exit_wr; |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | exit_wr: |
| 356 | /* Disable PG bit */ |
| 357 | STM32_FLASH_CR(bank) &= ~FLASH_CR_PG; |
| 358 | |
| 359 | lock(bank); |
| 360 | |
Nicolas Boichat | 63f9c77 | 2018-07-12 07:51:45 | [diff] [blame] | 361 | #ifdef CONFIG_ARMV7M_CACHE |
| 362 | /* Invalidate D-cache, to make sure we do not read back stale data. */ |
| 363 | cpu_clean_invalidate_dcache(); |
| 364 | #endif |
| 365 | |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 366 | return res; |
| 367 | } |
| 368 | |
Tim Lin | 670bd7e | 2021-06-04 06:11:25 | [diff] [blame] | 369 | int crec_flash_physical_erase(int offset, int size) |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 370 | { |
| 371 | int res = EC_SUCCESS; |
| 372 | int bank = offset / HWBANK_SIZE; |
| 373 | int last = (offset + size) / CONFIG_FLASH_ERASE_SIZE; |
| 374 | int sect; |
| 375 | |
| 376 | if (access_disabled) |
| 377 | return EC_ERROR_ACCESS_DENIED; |
| 378 | |
| 379 | /* work on a single hardware bank at a time */ |
Nicolas Boichat | 71fa029 | 2018-07-06 06:30:17 | [diff] [blame] | 380 | if ((offset + size - 1) / HWBANK_SIZE != bank) |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 381 | return EC_ERROR_INVAL; |
| 382 | |
| 383 | if (unlock(bank) != EC_SUCCESS) |
| 384 | return EC_ERROR_UNKNOWN; |
| 385 | |
| 386 | /* Clear previous error status */ |
| 387 | STM32_FLASH_CCR(bank) = FLASH_CCR_ERR_MASK; |
| 388 | |
| 389 | /* select erase parallelism */ |
Jack Rosenthal | 195d11b | 2022-06-27 20:28:58 | [diff] [blame] | 390 | STM32_FLASH_CR(bank) = (STM32_FLASH_CR(bank) & ~FLASH_CR_PSIZE_MASK) | |
| 391 | DEFAULT_PSIZE; |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 392 | |
| 393 | for (sect = offset / CONFIG_FLASH_ERASE_SIZE; sect < last; sect++) { |
| 394 | timestamp_t deadline; |
| 395 | |
| 396 | /* select page to erase and PER bit */ |
Jack Rosenthal | 195d11b | 2022-06-27 20:28:58 | [diff] [blame] | 397 | STM32_FLASH_CR(bank) = |
| 398 | (STM32_FLASH_CR(bank) & ~FLASH_CR_SNB_MASK) | |
| 399 | FLASH_CR_SER | FLASH_CR_SNB(sect); |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 400 | |
| 401 | /* set STRT bit : start erase */ |
| 402 | STM32_FLASH_CR(bank) |= FLASH_CR_STRT; |
| 403 | |
| 404 | /* |
| 405 | * Reload the watchdog timer to avoid watchdog reset during a |
| 406 | * long erase operation. |
| 407 | */ |
| 408 | watchdog_reload(); |
| 409 | |
| 410 | deadline.val = get_time().val + FLASH_ERASE_TIMEOUT_US; |
| 411 | /* Wait for erase to complete */ |
| 412 | while ((STM32_FLASH_SR(bank) & FLASH_SR_BUSY) && |
| 413 | (get_time().val < deadline.val)) { |
Patryk Duda | a31e654 | 2021-05-31 18:22:32 | [diff] [blame] | 414 | /* |
| 415 | * Interrupts may not be enabled, so we are using |
Patryk Duda | 85d7598 | 2024-04-10 13:10:59 | [diff] [blame] | 416 | * udelay() instead of crec_usleep() which can trigger |
Patryk Duda | a31e654 | 2021-05-31 18:22:32 | [diff] [blame] | 417 | * Forced Hard Fault (see b/180761547). |
| 418 | */ |
| 419 | udelay(5000); |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 420 | } |
| 421 | if (STM32_FLASH_SR(bank) & FLASH_SR_BUSY) { |
| 422 | res = EC_ERROR_TIMEOUT; |
| 423 | goto exit_er; |
| 424 | } |
| 425 | |
| 426 | /* |
| 427 | * Check for error conditions - erase failed, voltage error, |
| 428 | * protection error |
| 429 | */ |
| 430 | if (STM32_FLASH_SR(bank) & FLASH_CCR_ERR_MASK) { |
| 431 | res = EC_ERROR_UNKNOWN; |
| 432 | goto exit_er; |
| 433 | } |
| 434 | } |
| 435 | |
| 436 | exit_er: |
| 437 | /* reset SER bit */ |
| 438 | STM32_FLASH_CR(bank) &= ~(FLASH_CR_SER | FLASH_CR_SNB_MASK); |
| 439 | |
| 440 | lock(bank); |
| 441 | |
Nicolas Boichat | 63f9c77 | 2018-07-12 07:51:45 | [diff] [blame] | 442 | #ifdef CONFIG_ARMV7M_CACHE |
| 443 | /* Invalidate D-cache, to make sure we do not read back stale data. */ |
| 444 | cpu_clean_invalidate_dcache(); |
| 445 | #endif |
| 446 | |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 447 | return res; |
| 448 | } |
| 449 | |
Tim Lin | 670bd7e | 2021-06-04 06:11:25 | [diff] [blame] | 450 | int crec_flash_physical_get_protect(int block) |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 451 | { |
| 452 | int bank = block / BLOCKS_PER_HWBANK; |
| 453 | int index = block % BLOCKS_PER_HWBANK; |
| 454 | |
Gwendal Grignou | ac77140 | 2019-03-11 23:07:55 | [diff] [blame] | 455 | return !(STM32_FLASH_WPSN_CUR(bank) & BIT(index)); |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 456 | } |
| 457 | |
| 458 | /* |
| 459 | * Note: This does not need to update _NOW flags, as flash_get_protect |
| 460 | * in common code already does so. |
| 461 | */ |
Tim Lin | 670bd7e | 2021-06-04 06:11:25 | [diff] [blame] | 462 | uint32_t crec_flash_physical_get_protect_flags(void) |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 463 | { |
| 464 | uint32_t flags = 0; |
| 465 | |
| 466 | if (access_disabled) |
| 467 | flags |= EC_FLASH_PROTECT_ALL_NOW; |
| 468 | |
| 469 | if (is_wp_enabled()) |
| 470 | flags |= EC_FLASH_PROTECT_RO_AT_BOOT; |
| 471 | |
| 472 | /* Check if blocks were stuck locked at pre-init */ |
| 473 | if (stuck_locked) |
| 474 | flags |= EC_FLASH_PROTECT_ERROR_STUCK; |
| 475 | |
| 476 | return flags; |
| 477 | } |
| 478 | |
| 479 | #define WP_RANGE(start, count) (((1 << (count)) - 1) << (start)) |
| 480 | #define RO_WP_RANGE WP_RANGE(WP_BANK_OFFSET, WP_BANK_COUNT) |
| 481 | |
Wai-Hong Tam | 3bbdefd | 2025-06-10 20:30:48 | [diff] [blame] | 482 | int crec_flash_physical_protect_now(bool all) |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 483 | { |
| 484 | protect_blocks(RO_WP_RANGE); |
| 485 | |
| 486 | /* |
| 487 | * Lock the option bytes or the full access by writing a wrong |
| 488 | * key to FLASH_*KEYR. This triggers a bus fault, so we need to |
| 489 | * disable bus fault handler while doing this. |
| 490 | * |
| 491 | * This incorrect key fault causes the flash to become |
| 492 | * permanently locked until reset, a correct keyring write |
| 493 | * will not unlock it. |
| 494 | */ |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 495 | |
| 496 | if (all) { |
| 497 | /* cannot do any write/erase access until next reboot */ |
Josie Nordrum | 0574818 | 2020-12-06 19:32:14 | [diff] [blame] | 498 | disable_flash_control_register(); |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 499 | access_disabled = 1; |
| 500 | } |
| 501 | /* cannot modify the WP bits in the option bytes until reboot */ |
Josie Nordrum | 0574818 | 2020-12-06 19:32:14 | [diff] [blame] | 502 | disable_flash_option_bytes(); |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 503 | option_disabled = 1; |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 504 | |
| 505 | return EC_SUCCESS; |
| 506 | } |
| 507 | |
Tim Lin | 670bd7e | 2021-06-04 06:11:25 | [diff] [blame] | 508 | int crec_flash_physical_protect_at_boot(uint32_t new_flags) |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 509 | { |
| 510 | int new_wp_enable = !!(new_flags & EC_FLASH_PROTECT_RO_AT_BOOT); |
| 511 | |
| 512 | if (is_wp_enabled() != new_wp_enable) |
| 513 | return set_wp(new_wp_enable); |
| 514 | |
| 515 | return EC_SUCCESS; |
| 516 | } |
| 517 | |
Tim Lin | 670bd7e | 2021-06-04 06:11:25 | [diff] [blame] | 518 | uint32_t crec_flash_physical_get_valid_flags(void) |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 519 | { |
Jack Rosenthal | 195d11b | 2022-06-27 20:28:58 | [diff] [blame] | 520 | return EC_FLASH_PROTECT_RO_AT_BOOT | EC_FLASH_PROTECT_RO_NOW | |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 521 | EC_FLASH_PROTECT_ALL_NOW; |
| 522 | } |
| 523 | |
Tim Lin | 670bd7e | 2021-06-04 06:11:25 | [diff] [blame] | 524 | uint32_t crec_flash_physical_get_writable_flags(uint32_t cur_flags) |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 525 | { |
| 526 | uint32_t ret = 0; |
| 527 | |
| 528 | /* If RO protection isn't enabled, its at-boot state can be changed. */ |
| 529 | if (!(cur_flags & EC_FLASH_PROTECT_RO_NOW)) |
| 530 | ret |= EC_FLASH_PROTECT_RO_AT_BOOT; |
| 531 | |
| 532 | /* |
| 533 | * If entire flash isn't protected at this boot, it can be enabled if |
| 534 | * the WP GPIO is asserted. |
| 535 | */ |
| 536 | if (!(cur_flags & EC_FLASH_PROTECT_ALL_NOW) && |
| 537 | (cur_flags & EC_FLASH_PROTECT_GPIO_ASSERTED)) |
| 538 | ret |= EC_FLASH_PROTECT_ALL_NOW; |
| 539 | |
| 540 | return ret; |
| 541 | } |
| 542 | |
Tim Lin | 670bd7e | 2021-06-04 06:11:25 | [diff] [blame] | 543 | int crec_flash_physical_restore_state(void) |
Craig Hesling | 58bd6e3 | 2020-01-07 20:52:30 | [diff] [blame] | 544 | { |
| 545 | uint32_t reset_flags = system_get_reset_flags(); |
| 546 | int version, size; |
| 547 | const struct flash_wp_state *prev; |
| 548 | |
| 549 | /* |
| 550 | * If we have already jumped between images, an earlier image could |
| 551 | * have applied write protection. We simply need to represent these |
Jack Rosenthal | 195d11b | 2022-06-27 20:28:58 | [diff] [blame] | 552 | * irreversible flags to other components. |
Craig Hesling | 58bd6e3 | 2020-01-07 20:52:30 | [diff] [blame] | 553 | */ |
| 554 | if (reset_flags & EC_RESET_FLAG_SYSJUMP) { |
| 555 | prev = (const struct flash_wp_state *)system_get_jump_tag( |
Jack Rosenthal | 195d11b | 2022-06-27 20:28:58 | [diff] [blame] | 556 | FLASH_SYSJUMP_TAG, &version, &size); |
Craig Hesling | 58bd6e3 | 2020-01-07 20:52:30 | [diff] [blame] | 557 | if (prev && version == FLASH_HOOK_VERSION && |
| 558 | size == sizeof(*prev)) { |
| 559 | access_disabled = prev->access_disabled; |
| 560 | option_disabled = prev->option_disabled; |
| 561 | stuck_locked = prev->stuck_locked; |
| 562 | } |
| 563 | return 1; |
| 564 | } |
| 565 | |
| 566 | return 0; |
| 567 | } |
| 568 | |
Tim Lin | 670bd7e | 2021-06-04 06:11:25 | [diff] [blame] | 569 | int crec_flash_pre_init(void) |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 570 | { |
| 571 | uint32_t reset_flags = system_get_reset_flags(); |
Tim Lin | 670bd7e | 2021-06-04 06:11:25 | [diff] [blame] | 572 | uint32_t prot_flags = crec_flash_get_protect(); |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 573 | uint32_t unwanted_prot_flags = EC_FLASH_PROTECT_ALL_NOW | |
Jack Rosenthal | 195d11b | 2022-06-27 20:28:58 | [diff] [blame] | 574 | EC_FLASH_PROTECT_ERROR_INCONSISTENT; |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 575 | |
Tim Lin | 670bd7e | 2021-06-04 06:11:25 | [diff] [blame] | 576 | if (crec_flash_physical_restore_state()) |
Craig Hesling | 58bd6e3 | 2020-01-07 20:52:30 | [diff] [blame] | 577 | return EC_SUCCESS; |
| 578 | |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 579 | /* |
| 580 | * If we have already jumped between images, an earlier image could |
| 581 | * have applied write protection. Nothing additional needs to be done. |
| 582 | */ |
You-Cheng Syu | 555a447 | 2019-04-09 05:04:34 | [diff] [blame] | 583 | if (reset_flags & EC_RESET_FLAG_SYSJUMP) |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 584 | return EC_SUCCESS; |
| 585 | |
| 586 | if (prot_flags & EC_FLASH_PROTECT_GPIO_ASSERTED) { |
| 587 | /* |
| 588 | * Write protect is asserted. If we want RO flash protected, |
| 589 | * protect it now. |
| 590 | */ |
| 591 | if ((prot_flags & EC_FLASH_PROTECT_RO_AT_BOOT) && |
| 592 | !(prot_flags & EC_FLASH_PROTECT_RO_NOW)) { |
Nicolas Boichat | c98ea9a | 2018-09-12 21:39:39 | [diff] [blame] | 593 | int rv; |
| 594 | |
Tim Lin | 670bd7e | 2021-06-04 06:11:25 | [diff] [blame] | 595 | rv = crec_flash_set_protect(EC_FLASH_PROTECT_RO_NOW, |
| 596 | EC_FLASH_PROTECT_RO_NOW); |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 597 | if (rv) |
| 598 | return rv; |
| 599 | |
| 600 | /* Re-read flags */ |
Tim Lin | 670bd7e | 2021-06-04 06:11:25 | [diff] [blame] | 601 | prot_flags = crec_flash_get_protect(); |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 602 | } |
| 603 | } else { |
| 604 | /* Don't want RO flash protected */ |
| 605 | unwanted_prot_flags |= EC_FLASH_PROTECT_RO_NOW; |
| 606 | } |
| 607 | |
| 608 | /* If there are no unwanted flags, done */ |
| 609 | if (!(prot_flags & unwanted_prot_flags)) |
| 610 | return EC_SUCCESS; |
| 611 | |
| 612 | /* |
| 613 | * If the last reboot was a power-on reset, it should have cleared |
| 614 | * write-protect. If it didn't, then the flash write protect registers |
| 615 | * have been permanently committed and we can't fix that. |
| 616 | */ |
You-Cheng Syu | 555a447 | 2019-04-09 05:04:34 | [diff] [blame] | 617 | if (reset_flags & EC_RESET_FLAG_POWER_ON) { |
Vincent Palatin | 730491d | 2018-01-30 16:01:50 | [diff] [blame] | 618 | stuck_locked = 1; |
| 619 | return EC_ERROR_ACCESS_DENIED; |
| 620 | } |
| 621 | |
| 622 | /* Otherwise, do a hard boot to clear the flash protection registers */ |
| 623 | system_reset(SYSTEM_RESET_HARD | SYSTEM_RESET_PRESERVE_FLAGS); |
| 624 | |
| 625 | /* That doesn't return, so if we're still here that's an error */ |
| 626 | return EC_ERROR_UNKNOWN; |
| 627 | } |
Craig Hesling | 58bd6e3 | 2020-01-07 20:52:30 | [diff] [blame] | 628 | |
| 629 | /*****************************************************************************/ |
| 630 | /* Hooks */ |
| 631 | |
| 632 | static void flash_preserve_state(void) |
| 633 | { |
| 634 | const struct flash_wp_state state = { |
| 635 | .access_disabled = access_disabled, |
| 636 | .option_disabled = option_disabled, |
| 637 | .stuck_locked = stuck_locked, |
| 638 | }; |
| 639 | |
| 640 | system_add_jump_tag(FLASH_SYSJUMP_TAG, FLASH_HOOK_VERSION, |
| 641 | sizeof(state), &state); |
| 642 | } |
| 643 | DECLARE_HOOK(HOOK_SYSJUMP, flash_preserve_state, HOOK_PRIO_DEFAULT); |