Arrow / Mbed OS DAPLink Reset
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers iap_flash_intf.c Source File

iap_flash_intf.c

Go to the documentation of this file.
00001 /**
00002  * @file    iap_flash_intf.c
00003  * @brief   Implementation of flash_intf.h
00004  *
00005  * DAPLink Interface Firmware
00006  * Copyright (c) 2009-2019, ARM Limited, All Rights Reserved
00007  * SPDX-License-Identifier: Apache-2.0
00008  *
00009  * Licensed under the Apache License, Version 2.0 (the "License"); you may
00010  * not use this file except in compliance with the License.
00011  * You may obtain a copy of the License at
00012  *
00013  * http://www.apache.org/licenses/LICENSE-2.0
00014  *
00015  * Unless required by applicable law or agreed to in writing, software
00016  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00017  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00018  * See the License for the specific language governing permissions and
00019  * limitations under the License.
00020  */
00021 
00022 #include <string.h>
00023 
00024 #include "daplink.h"
00025 #include "flash_intf.h"
00026 #include "util.h"
00027 #include "flash_hal.h"
00028 #include "FlashPrg.h"
00029 #include "compiler.h"
00030 #include "crc.h"
00031 #include "info.h"
00032 
00033 // Application start must be aligned to page write
00034 COMPILER_ASSERT(DAPLINK_ROM_APP_START % DAPLINK_MIN_WRITE_SIZE == 0);
00035 // Application size must be a multiple of write size
00036 COMPILER_ASSERT(DAPLINK_ROM_APP_SIZE % DAPLINK_MIN_WRITE_SIZE == 0);
00037 // Sector size must be a multiple of write size
00038 COMPILER_ASSERT(DAPLINK_SECTOR_SIZE % DAPLINK_MIN_WRITE_SIZE == 0);
00039 // Application start must be aligned to a sector erase
00040 COMPILER_ASSERT(DAPLINK_ROM_APP_START % DAPLINK_SECTOR_SIZE == 0);
00041 // Update start must be aligned to sector write
00042 COMPILER_ASSERT(DAPLINK_ROM_UPDATE_START % DAPLINK_SECTOR_SIZE == 0);
00043 // Update size must be a multiple of sector size
00044 COMPILER_ASSERT(DAPLINK_ROM_UPDATE_SIZE % DAPLINK_SECTOR_SIZE == 0);
00045 
00046 typedef enum {
00047     STATE_CLOSED,
00048     STATE_OPEN,
00049     STATE_ERROR
00050 } state_t;
00051 
00052 static error_t init(void);
00053 static error_t uninit(void);
00054 static error_t program_page(uint32_t addr, const uint8_t *buf, uint32_t size);
00055 static error_t erase_sector(uint32_t addr);
00056 static error_t erase_chip(void);
00057 static uint32_t program_page_min_size(uint32_t addr);
00058 static uint32_t erase_sector_size(uint32_t addr);
00059 
00060 static bool page_program_allowed(uint32_t addr, uint32_t size);
00061 static bool sector_erase_allowed(uint32_t addr);
00062 static error_t intercept_page_write(uint32_t addr, const uint8_t *buf, uint32_t size);
00063 static error_t intercept_sector_erase(uint32_t addr);
00064 static error_t critical_erase_and_program(uint32_t addr, const uint8_t *data, uint32_t size);
00065 static uint8_t target_flash_busy(void);
00066 
00067 static const flash_intf_t flash_intf = {
00068     init,
00069     uninit,
00070     program_page,
00071     erase_sector,
00072     erase_chip,
00073     program_page_min_size,
00074     erase_sector_size,
00075     target_flash_busy,
00076 };
00077 
00078 const flash_intf_t *const flash_intf_iap_protected = &flash_intf;
00079 
00080 static state_t state = STATE_CLOSED;
00081 static bool update_complete;
00082 static bool mass_erase_performed;
00083 static bool current_sector_set;
00084 static uint32_t current_sector;
00085 static uint32_t current_sector_size;
00086 static bool current_page_set;
00087 static uint32_t current_page;
00088 static uint32_t current_page_write_size;
00089 static uint32_t crc;
00090 static uint8_t sector_buf[DAPLINK_SECTOR_SIZE];
00091 
00092 static error_t init()
00093 {
00094     int iap_status;
00095     bool update_supported = DAPLINK_ROM_UPDATE_SIZE != 0;
00096 
00097     if (state != STATE_CLOSED) {
00098         util_assert(0);
00099         return ERROR_INTERNAL;
00100     }
00101 
00102     if (!update_supported) {
00103         return ERROR_IAP_UPDT_NOT_SUPPORTED;
00104     }
00105 
00106     iap_status = Init(0, 0, 0);
00107 
00108     if (iap_status != 0) {
00109         return ERROR_IAP_INIT;
00110     }
00111 
00112     update_complete = false;
00113     mass_erase_performed = false;
00114     current_sector_set = false;
00115     current_sector = 0;
00116     current_sector_size = 0;
00117     current_page_set = false;
00118     current_page = 0;
00119     current_page_write_size = 0;
00120     crc = 0;
00121     memset(sector_buf, 0, sizeof(sector_buf));
00122     state = STATE_OPEN;
00123     return ERROR_SUCCESS;
00124 }
00125 
00126 static error_t uninit(void)
00127 {
00128     int iap_status;
00129 
00130     if (STATE_CLOSED == state) {
00131         util_assert(0);
00132         return ERROR_INTERNAL;
00133     }
00134 
00135     state = STATE_CLOSED;
00136     iap_status = UnInit(0);
00137 
00138     if (iap_status != 0) {
00139         return ERROR_IAP_UNINIT;
00140     }
00141 
00142     if (!update_complete && !daplink_is_bootloader()) {
00143         // Interface - Error if the bootloader update is not complete
00144         // Bootloader - For 3rd party applications the end of the update
00145         //              is unknown so it is not an error if the transfer
00146         //              ends early.
00147         return ERROR_IAP_UPDT_INCOMPLETE;
00148     }
00149 
00150     return ERROR_SUCCESS;
00151 }
00152 
00153 static error_t program_page(uint32_t addr, const uint8_t *buf, uint32_t size)
00154 {
00155     uint32_t iap_status;
00156     error_t status;
00157     uint32_t min_prog_size;
00158     uint32_t sector_size;
00159     uint32_t updt_end = DAPLINK_ROM_UPDATE_START + DAPLINK_ROM_UPDATE_SIZE;
00160 
00161     if (state != STATE_OPEN) {
00162         util_assert(0);
00163         return ERROR_INTERNAL;
00164     }
00165 
00166     min_prog_size = program_page_min_size(addr);
00167     sector_size = erase_sector_size(addr);
00168 
00169     // Address must be on a write size boundary
00170     if (addr % min_prog_size != 0) {
00171         util_assert(0);
00172         state = STATE_ERROR;
00173         return ERROR_INTERNAL;
00174     }
00175 
00176     // Programming size must be a non-zero multiple of the minimum write size
00177     if ((size < min_prog_size) || (size % min_prog_size != 0)) {
00178         util_assert(0);
00179         state = STATE_ERROR;
00180         return ERROR_INTERNAL;
00181     }
00182 
00183     // Write must not cross a sector boundary
00184     if ((addr % sector_size) + size > sector_size) {
00185         util_assert(0);
00186         state = STATE_ERROR;
00187         return ERROR_INTERNAL;
00188     }
00189 
00190     // Write must be in an erased sector (current_sector is always erased if it is set)
00191     if (!mass_erase_performed) {
00192         if (!current_sector_set) {
00193             util_assert(0);
00194             state = STATE_ERROR;
00195             return ERROR_INTERNAL;
00196         }
00197 
00198         if ((addr < current_sector) || (addr >= current_sector + current_sector_size)) {
00199             util_assert(0);
00200             state = STATE_ERROR;
00201             return ERROR_INTERNAL;
00202         }
00203     }
00204 
00205     // Address must be sequential - no gaps
00206     if (current_page_set && (addr != current_page + current_page_write_size)) {
00207         util_assert(0);
00208         state = STATE_ERROR;
00209         return ERROR_INTERNAL;
00210     }
00211 
00212     if (!page_program_allowed(addr, size)) {
00213         state = STATE_ERROR;
00214         return ERROR_IAP_WRITE;
00215     }
00216 
00217     current_page_set = true;
00218     current_page = addr;
00219     current_page_write_size = size;
00220     status = intercept_page_write(addr, buf, size);
00221 
00222     if (status != ERROR_IAP_NO_INTERCEPT) {
00223         // The operation has been intercepted so
00224         // return the result
00225         if (ERROR_SUCCESS != status) {
00226             state = STATE_ERROR;
00227         }
00228 
00229         return status;
00230     }
00231 
00232     iap_status = flash_program_page(addr, size, (uint8_t *)buf);
00233 
00234     if (iap_status != 0) {
00235         state = STATE_ERROR;
00236         return ERROR_IAP_WRITE;
00237     }
00238 
00239     if (addr + size >= updt_end) {
00240         // Something has been updated so recompute the crc
00241         info_crc_compute();
00242         update_complete = true;
00243     }
00244 
00245     return ERROR_SUCCESS;
00246 }
00247 
00248 static error_t erase_sector(uint32_t addr)
00249 {
00250     uint32_t iap_status;
00251     error_t status;
00252     uint32_t sector_size;
00253 
00254     if (state != STATE_OPEN) {
00255         util_assert(0);
00256         return ERROR_INTERNAL;
00257     }
00258 
00259     // Address must be on a sector boundary
00260     sector_size = erase_sector_size(addr);
00261 
00262     if (addr % sector_size != 0) {
00263         util_assert(0);
00264         state = STATE_ERROR;
00265         return ERROR_INTERNAL;
00266     }
00267 
00268     // Address must be sequential - no gaps
00269     if (current_sector_set && (addr != current_sector + current_sector_size)) {
00270         util_assert(0);
00271         state = STATE_ERROR;
00272         return ERROR_INTERNAL;
00273     }
00274 
00275     if (!sector_erase_allowed(addr)) {
00276         state = STATE_ERROR;
00277         return ERROR_IAP_ERASE_SECTOR;
00278     }
00279 
00280     current_sector_set = true;
00281     current_sector = addr;
00282     current_sector_size = sector_size;
00283     status = intercept_sector_erase(addr);
00284 
00285     if (status != ERROR_IAP_NO_INTERCEPT) {
00286         // The operation has been intercepted so
00287         // return the result
00288         if (ERROR_SUCCESS != status) {
00289             state = STATE_ERROR;
00290         }
00291 
00292         return status;
00293     }
00294 
00295     iap_status = flash_erase_sector(addr);
00296 
00297     if (iap_status != 0) {
00298         state = STATE_ERROR;
00299         return ERROR_IAP_ERASE_SECTOR;
00300     }
00301 
00302     return ERROR_SUCCESS;
00303 }
00304 
00305 static error_t erase_chip(void)
00306 {
00307     uint32_t updt_start = DAPLINK_ROM_UPDATE_START;
00308     uint32_t updt_end = DAPLINK_ROM_UPDATE_START + DAPLINK_ROM_UPDATE_SIZE;
00309 
00310     if (state != STATE_OPEN) {
00311         util_assert(0);
00312         return ERROR_INTERNAL;
00313     }
00314 
00315     if (mass_erase_performed) {
00316         // Mass erase only allowed once
00317         util_assert(0);
00318         state = STATE_ERROR;
00319         return ERROR_INTERNAL;
00320     }
00321 
00322     for (uint32_t addr = updt_start; addr < updt_end; addr += DAPLINK_SECTOR_SIZE) {
00323         error_t status;
00324         status = erase_sector(addr);
00325 
00326         if (status != ERROR_SUCCESS) {
00327             state = STATE_ERROR;
00328             return ERROR_IAP_ERASE_ALL;
00329         }
00330     }
00331 
00332     mass_erase_performed = true;
00333     return ERROR_SUCCESS;
00334 }
00335 
00336 static uint32_t program_page_min_size(uint32_t addr)
00337 {
00338     return DAPLINK_MIN_WRITE_SIZE;
00339 }
00340 
00341 static uint32_t erase_sector_size(uint32_t addr)
00342 {
00343     return DAPLINK_SECTOR_SIZE;
00344 }
00345 
00346 static bool page_program_allowed(uint32_t addr, uint32_t size)
00347 {
00348     // Check if any data would overlap with the application region
00349     if ((addr < DAPLINK_ROM_APP_START + DAPLINK_ROM_APP_SIZE) && (addr + size > DAPLINK_ROM_APP_START)) {
00350         return false;
00351     }
00352 
00353     return true;
00354 }
00355 
00356 static bool sector_erase_allowed(uint32_t addr)
00357 {
00358     uint32_t app_start = DAPLINK_ROM_APP_START;
00359     uint32_t app_end = DAPLINK_ROM_APP_START + DAPLINK_ROM_APP_SIZE;
00360 
00361     // Check if the sector is part of the application
00362     if ((addr >= app_start) && (addr < app_end)) {
00363         return false;
00364     }
00365 
00366     return true;
00367 }
00368 
00369 static error_t intercept_page_write(uint32_t addr, const uint8_t *buf, uint32_t size)
00370 {
00371     error_t status;
00372     uint32_t crc_size;
00373     uint32_t updt_start = DAPLINK_ROM_UPDATE_START;
00374     uint32_t updt_end = DAPLINK_ROM_UPDATE_START + DAPLINK_ROM_UPDATE_SIZE;
00375 
00376     if (state != STATE_OPEN) {
00377         util_assert(0);
00378         return ERROR_INTERNAL;
00379     }
00380 
00381     if ((addr < updt_start) || (addr >= updt_end)) {
00382         return ERROR_IAP_OUT_OF_BOUNDS;
00383     }
00384 
00385     if (!daplink_is_interface()) {
00386         return ERROR_IAP_NO_INTERCEPT;
00387     }
00388 
00389     /* Everything below here is interface specific */
00390     crc_size = MIN(size, updt_end - addr - 4);
00391     crc = crc32_continue(crc, buf, crc_size);
00392 
00393     // Intercept the data if it is in the first sector
00394     if ((addr >= updt_start) && (addr < updt_start + DAPLINK_SECTOR_SIZE)) {
00395         uint32_t buf_offset = addr - updt_start;
00396         memcpy(sector_buf + buf_offset, buf, size);
00397         // Intercept was successful
00398         return ERROR_SUCCESS;
00399     }
00400 
00401     // Finalize update if this is the last sector
00402     if (updt_end == addr + size) {
00403         uint32_t iap_status;
00404         uint32_t size_left = updt_end - addr;
00405         uint32_t crc_in_image = (buf[size_left - 4] << 0) |
00406                                 (buf[size_left - 3] << 8) |
00407                                 (buf[size_left - 2] << 16) |
00408                                 (buf[size_left - 1] << 24);
00409 
00410         if (crc != crc_in_image) {
00411             return ERROR_BL_UPDT_BAD_CRC;
00412         }
00413 
00414         // Program the current buffer
00415         iap_status = flash_program_page(addr, size, (uint8_t *)buf);
00416 
00417         if (iap_status != 0) {
00418             return ERROR_IAP_WRITE;
00419         }
00420 
00421         status = critical_erase_and_program(DAPLINK_ROM_UPDATE_START, sector_buf, DAPLINK_SECTOR_SIZE);
00422 
00423         if (ERROR_SUCCESS == status) {
00424             status = ERROR_SUCCESS;
00425         }
00426 
00427         // The bootloader has been updated so recompute the crc
00428         info_crc_compute();
00429         update_complete = true;
00430         return status;
00431     }
00432 
00433     return ERROR_IAP_NO_INTERCEPT;
00434 }
00435 
00436 static error_t intercept_sector_erase(uint32_t addr)
00437 {
00438     error_t status;
00439     uint32_t updt_start = DAPLINK_ROM_UPDATE_START;
00440     uint32_t updt_end = DAPLINK_ROM_UPDATE_START + DAPLINK_ROM_UPDATE_SIZE;
00441 
00442     if (state != STATE_OPEN) {
00443         util_assert(0);
00444         return ERROR_INTERNAL;
00445     }
00446 
00447     if ((addr < updt_start) || (addr >= updt_end)) {
00448         return ERROR_IAP_OUT_OF_BOUNDS;
00449     }
00450 
00451     if (!daplink_is_interface()) {
00452         return ERROR_IAP_NO_INTERCEPT;
00453     }
00454 
00455     /* Everything below here is interface specific */
00456 
00457     if (DAPLINK_ROM_UPDATE_START == addr) {
00458         uint32_t addr = DAPLINK_ROM_UPDATE_START;
00459         status = critical_erase_and_program(addr, (uint8_t *)DAPLINK_ROM_IF_START, DAPLINK_MIN_WRITE_SIZE);
00460 
00461         if (ERROR_SUCCESS == status) {
00462             // Intercept was successful
00463             status = ERROR_SUCCESS;
00464         }
00465 
00466         return status;
00467     }
00468 
00469     return ERROR_IAP_NO_INTERCEPT;
00470 }
00471 
00472 static error_t critical_erase_and_program(uint32_t addr, const uint8_t *data, uint32_t size)
00473 {
00474     uint32_t iap_status;
00475 
00476     if (size < DAPLINK_MIN_WRITE_SIZE) {
00477         util_assert(0);
00478         return ERROR_INTERNAL;
00479     }
00480 
00481     // CRITICAL SECTION BELOW HERE!
00482     // If something goes wrong with either
00483     // the erase or write then the device
00484     // will no longer be bootable.
00485     // Erase the first sector
00486     iap_status = flash_erase_sector(addr);
00487 
00488     if (iap_status != 0) {
00489         return ERROR_ERASE_ALL;
00490     }
00491 
00492     // Program the interface's vector table
00493     iap_status = flash_program_page(addr, size, (uint8_t *)data);
00494 
00495     if (iap_status != 0) {
00496         return ERROR_IAP_WRITE;
00497     }
00498 
00499     return ERROR_SUCCESS;
00500 }
00501 
00502 static uint8_t target_flash_busy(void){
00503     return (state == STATE_OPEN);
00504 }