Mayank Gupta / Mbed OS pelion-example-frdm

Dependencies:   FXAS21002 FXOS8700Q

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers arm_uc_pal_blockdevice_implementation.c Source File

arm_uc_pal_blockdevice_implementation.c

00001 // ----------------------------------------------------------------------------
00002 // Copyright 2017-2018 ARM Ltd.
00003 //
00004 // SPDX-License-Identifier: Apache-2.0
00005 //
00006 // Licensed under the Apache License, Version 2.0 (the "License");
00007 // you may not use this file except in compliance with the License.
00008 // You may obtain a copy of the License at
00009 //
00010 //     http://www.apache.org/licenses/LICENSE-2.0
00011 //
00012 // Unless required by applicable law or agreed to in writing, software
00013 // distributed under the License is distributed on an "AS IS" BASIS,
00014 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015 // See the License for the specific language governing permissions and
00016 // limitations under the License.
00017 // ----------------------------------------------------------------------------
00018 
00019 #include "arm_uc_config.h"
00020 #if defined(ARM_UC_FEATURE_PAL_BLOCKDEVICE) && (ARM_UC_FEATURE_PAL_BLOCKDEVICE == 1)
00021 
00022 #define __STDC_FORMAT_MACROS
00023 
00024 #include "update-client-pal-blockdevice/arm_uc_pal_blockdevice.h"
00025 #include "update-client-pal-blockdevice/arm_uc_pal_blockdevice_platform.h"
00026 
00027 #include "update-client-metadata-header/arm_uc_metadata_header_v2.h"
00028 
00029 #include <inttypes.h>
00030 
00031 #ifndef MBED_CONF_UPDATE_CLIENT_STORAGE_ADDRESS
00032 #define MBED_CONF_UPDATE_CLIENT_STORAGE_ADDRESS 0
00033 #endif
00034 
00035 #ifndef MBED_CONF_UPDATE_CLIENT_STORAGE_SIZE
00036 #define MBED_CONF_UPDATE_CLIENT_STORAGE_SIZE 0
00037 #endif
00038 
00039 #ifndef MBED_CONF_UPDATE_CLIENT_STORAGE_PAGE
00040 #define MBED_CONF_UPDATE_CLIENT_STORAGE_PAGE 1
00041 #endif
00042 
00043 #ifndef MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS
00044 #define MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS 1
00045 #endif
00046 
00047 /* consistency check */
00048 #if (MBED_CONF_UPDATE_CLIENT_STORAGE_PAGE == 0)
00049 #error Update client storage page cannot be zero.
00050 #endif
00051 
00052 #if (MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS == 0)
00053 #error Update client storage locations must be at least 1.
00054 #endif
00055 
00056 #if (MBED_CONF_UPDATE_CLIENT_STORAGE_SIZE == 0)
00057 #error Update client storage size cannot be zero.
00058 #endif
00059 
00060 /* Check that the statically allocated buffers are aligned with the block size */
00061 #define ARM_UC_PAL_ONE_BUFFER (ARM_UC_BUFFER_SIZE / 2)
00062 #define ARM_UC_PAL_PAGES (ARM_UC_PAL_ONE_BUFFER / MBED_CONF_UPDATE_CLIENT_STORAGE_PAGE)
00063 
00064 #if !((ARM_UC_PAL_PAGES * MBED_CONF_UPDATE_CLIENT_STORAGE_PAGE) == ARM_UC_PAL_ONE_BUFFER)
00065 #error Update client buffer must be divisible by the block page size
00066 #endif
00067 
00068 static ARM_UC_PAAL_UPDATE_SignalEvent_t pal_blockdevice_event_handler = NULL;
00069 static uint32_t pal_blockdevice_firmware_size = 0;
00070 static uint32_t pal_blockdevice_page_size = 0;
00071 static uint32_t pal_blockdevice_sector_size = 0;
00072 static uint32_t pal_blockdevice_hdr_size = 0;
00073 
00074 static void pal_blockdevice_signal_internal(uint32_t event)
00075 {
00076     if (pal_blockdevice_event_handler) {
00077         pal_blockdevice_event_handler(event);
00078     }
00079 }
00080 
00081 /**
00082  * @brief Round size up to nearest page
00083  *
00084  * @param size The size that need to be rounded up
00085  * @return Returns the size rounded up to the nearest page
00086  */
00087 static uint32_t pal_blockdevice_round_up_to_page(uint32_t size)
00088 {
00089     uint32_t round_up = 0;
00090     /* 0 is an aligned address and math operation below would not return 0.
00091        It would return pal_blockdevice_page_size*/
00092     if (size != 0){
00093         round_up = ((size - 1) / pal_blockdevice_page_size + 1) * pal_blockdevice_page_size;
00094     }
00095     return round_up;
00096 }
00097 
00098 /**
00099  * @brief Round size down to nearest page
00100  *
00101  * @param size The size that need to be rounded up
00102  * @return Returns the size rounded up to the nearest page
00103  */
00104 static uint32_t pal_blockdevice_round_down_to_page(uint32_t size)
00105 {
00106     return (size / pal_blockdevice_page_size) * pal_blockdevice_page_size;
00107 }
00108 
00109 /**
00110  * @brief Align size up to sector size
00111  *
00112  * @param size The size that need to be rounded up
00113  * @return Returns the size aligned to sector size
00114  */
00115 static uint32_t pal_blockdevice_round_up_to_sector(uint32_t size)
00116 {
00117     uint32_t round_up = 0;
00118     /* 0 is an aligned address and math operation below would not return 0.
00119        It would return pal_blockdevice_sector_size*/
00120     if (size != 0){
00121         round_up = ((size - 1) / pal_blockdevice_sector_size + 1) * pal_blockdevice_sector_size;
00122     }
00123     return round_up;
00124 }
00125 
00126 /**
00127  * @brief Align size down to sector size
00128  *
00129  * @param size The size that need to be rounded up
00130  * @return Returns the size aligned to sector boundary
00131  */
00132 static uint32_t pal_blockdevice_round_down_to_sector(uint32_t size)
00133 {
00134     return (size / pal_blockdevice_sector_size) * pal_blockdevice_sector_size;
00135 }
00136 
00137 /**
00138  * @brief Get the physicl slot address and size given slot_id
00139  *
00140  * @param slot_id Storage location ID.
00141  * @param slot_addr the slot address is returned in this pointer
00142  * @param slot_size the slot size is returned in this pointer
00143  * @return Returns ERR_NONE on success.
00144  *         Returns ERR_INVALID_PARAMETER on error.
00145  */
00146 static arm_uc_error_t pal_blockdevice_get_slot_addr_size(uint32_t slot_id,
00147                                                          uint32_t *slot_addr,
00148                                                          uint32_t *slot_size)
00149 {
00150     arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER };
00151 
00152     if ((slot_id < MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS) &&
00153             (slot_addr != NULL) &&
00154             (slot_size != NULL)) {
00155         /* find the start address of the whole storage area. It needs to be aligned to
00156            sector boundary and we cannot go outside user defined storage area, hence
00157            rounding up to sector boundary */
00158         uint32_t storage_start_addr = pal_blockdevice_round_up_to_sector(
00159                                           MBED_CONF_UPDATE_CLIENT_STORAGE_ADDRESS);
00160         /* find the end address of the whole storage area. It needs to be aligned to
00161            sector boundary and we cannot go outside user defined storage area, hence
00162            rounding down to sector boundary */
00163         uint32_t storage_end_addr = pal_blockdevice_round_down_to_sector(
00164                                         MBED_CONF_UPDATE_CLIENT_STORAGE_ADDRESS + \
00165                                         MBED_CONF_UPDATE_CLIENT_STORAGE_SIZE);
00166         /* find the maximum size each slot can have given the start and end, without
00167            considering the alignment of individual slots */
00168         uint32_t max_slot_size = (storage_end_addr - storage_start_addr) / \
00169                                  MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS;
00170         /* find the start address of slot. It needs to align to sector boundary. We
00171            choose here to round down at each slot boundary */
00172         *slot_addr = storage_start_addr + pal_blockdevice_round_down_to_sector(
00173                          slot_id * max_slot_size);
00174         /* Rounding down slot size to sector boundary same as
00175            the slot start address so that we make sure two slot don't overlap */
00176         *slot_size  = pal_blockdevice_round_down_to_sector(max_slot_size);
00177         result.code = ERR_NONE;
00178     }
00179 
00180     return result;
00181 }
00182 
00183 /**
00184  * @brief Initialize the underlying storage and set the callback handler.
00185  *
00186  * @param callback Function pointer to event handler.
00187  * @return Returns ERR_NONE on accept, and signals the event handler with
00188  *         either DONE or ERROR when complete.
00189  *         Returns ERR_INVALID_PARAMETER on reject, and no signal is sent.
00190  */
00191 arm_uc_error_t ARM_UC_PAL_BlockDevice_Initialize(ARM_UC_PAAL_UPDATE_SignalEvent_t callback)
00192 {
00193     arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER };
00194 
00195     if (callback) {
00196         UC_PAAL_TRACE("ARM_UC_PAL_BlockDevice_Initialize");
00197 
00198         int status = arm_uc_blockdevice_init();
00199         pal_blockdevice_page_size  = arm_uc_blockdevice_get_program_size();
00200         pal_blockdevice_sector_size = arm_uc_blockdevice_get_erase_size();
00201         pal_blockdevice_hdr_size   = pal_blockdevice_round_up_to_page(ARM_UC_EXTERNAL_HEADER_SIZE_V2);
00202 
00203         if (status == ARM_UC_BLOCKDEVICE_SUCCESS) {
00204             pal_blockdevice_event_handler = callback;
00205             pal_blockdevice_signal_internal(ARM_UC_PAAL_EVENT_INITIALIZE_DONE);
00206             result.code = ERR_NONE;
00207         }
00208     }
00209 
00210     return result;
00211 }
00212 
00213 /**
00214  * @brief Get maximum number of supported storage locations.
00215  *
00216  * @return Number of storage locations.
00217  */
00218 uint32_t ARM_UC_PAL_BlockDevice_GetMaxID(void)
00219 {
00220     return MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS;
00221 }
00222 
00223 /**
00224  * @brief Prepare the storage layer for a new firmware image.
00225  * @details The storage location is set up to receive an image with
00226  *          the details passed in the details struct.
00227  *
00228  * @param slot_id Storage location ID.
00229  * @param details Pointer to a struct with firmware details.
00230  * @param buffer Temporary buffer for formatting and storing metadata.
00231  * @return Returns ERR_NONE on accept, and signals the event handler with
00232  *         either DONE or ERROR when complete.
00233  *         Returns ERR_INVALID_PARAMETER on reject, and no signal is sent.
00234  */
00235 arm_uc_error_t ARM_UC_PAL_BlockDevice_Prepare(uint32_t slot_id,
00236                                               const arm_uc_firmware_details_t *details,
00237                                               arm_uc_buffer_t *buffer)
00238 {
00239     arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER };
00240 
00241     if (details && buffer && buffer->ptr) {
00242         UC_PAAL_TRACE("ARM_UC_PAL_BlockDevice_Prepare: %" PRIX32 " %" PRIX64,
00243                       slot_id, details->size);
00244 
00245         /* encode firmware details in buffer */
00246         arm_uc_error_t header_status = arm_uc_create_external_header_v2(details,
00247                                                                         buffer);
00248         if (header_status.error == ERR_NONE) {
00249             /* find the size needed to erase. Header is stored contiguous with firmware */
00250             uint32_t erase_size = pal_blockdevice_round_up_to_sector(pal_blockdevice_hdr_size + \
00251                                                                      details->size);
00252 
00253             /* find address of slot */
00254             uint32_t slot_addr = ARM_UC_BLOCKDEVICE_INVALID_SIZE;
00255             uint32_t slot_size = ARM_UC_BLOCKDEVICE_INVALID_SIZE;
00256             result = pal_blockdevice_get_slot_addr_size(slot_id, &slot_addr, &slot_size);
00257 
00258             UC_PAAL_TRACE("erase: %" PRIX32 " %" PRIX32 " %" PRIX32, slot_addr, erase_size, slot_size);
00259 
00260             int status = ARM_UC_BLOCKDEVICE_FAIL;
00261             if (result.error == ERR_NONE) {
00262                 if (erase_size <= slot_size) {
00263                     /* erase */
00264                     status = arm_uc_blockdevice_erase(slot_addr, erase_size);
00265                 } else {
00266                     UC_PAAL_ERR_MSG("not enough space for firmware image");
00267                     result.code = PAAL_ERR_FIRMWARE_TOO_LARGE;
00268                 }
00269             }
00270 
00271             if (status == ARM_UC_BLOCKDEVICE_SUCCESS) {
00272                 /* write header */
00273                 status = arm_uc_blockdevice_program(buffer->ptr,
00274                                                     slot_addr,
00275                                                     pal_blockdevice_hdr_size);
00276 
00277                 if (status == ARM_UC_BLOCKDEVICE_SUCCESS) {
00278                     /* set return code */
00279                     result.code = ERR_NONE;
00280 
00281                     /* store firmware size in global */
00282                     pal_blockdevice_firmware_size = details->size;
00283 
00284                     /* signal done */
00285                     pal_blockdevice_signal_internal(ARM_UC_PAAL_EVENT_PREPARE_DONE);
00286                 } else {
00287                     UC_PAAL_ERR_MSG("arm_uc_blockdevice_program failed");
00288                 }
00289             } else {
00290                 UC_PAAL_ERR_MSG("arm_uc_blockdevice_erase failed");
00291             }
00292         } else {
00293             UC_PAAL_ERR_MSG("arm_uc_create_external_header_v2 failed");
00294         }
00295     }
00296 
00297     return result;
00298 }
00299 
00300 /**
00301  * @brief Write a fragment to the indicated storage location.
00302  * @details The storage location must have been allocated using the Prepare
00303  *          call. The call is expected to write the entire fragment before
00304  *          signaling completion.
00305  *
00306  * @param slot_id Storage location ID.
00307  * @param offset Offset in bytes to where the fragment should be written.
00308  * @param buffer Pointer to buffer struct with fragment.
00309  * @return Returns ERR_NONE on accept, and signals the event handler with
00310  *         either DONE or ERROR when complete.
00311  *         Returns ERR_INVALID_PARAMETER on reject, and no signal is sent.
00312  */
00313 arm_uc_error_t ARM_UC_PAL_BlockDevice_Write(uint32_t slot_id,
00314                                             uint32_t offset,
00315                                             const arm_uc_buffer_t *buffer)
00316 {
00317     arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER };
00318 
00319     if (buffer && buffer->ptr) {
00320         UC_PAAL_TRACE("ARM_UC_PAL_BlockDevice_Write: %" PRIX32 " %" PRIX32 " %" PRIX32,
00321                       slot_id, offset, buffer->size);
00322         int status = ARM_UC_BLOCKDEVICE_SUCCESS;
00323 
00324         /* find address of slot */
00325         uint32_t slot_addr = ARM_UC_BLOCKDEVICE_INVALID_SIZE;
00326         uint32_t slot_size = ARM_UC_BLOCKDEVICE_INVALID_SIZE;
00327         result = pal_blockdevice_get_slot_addr_size(slot_id, &slot_addr, &slot_size);
00328         uint32_t physical_address = slot_addr + pal_blockdevice_hdr_size + offset;
00329 
00330         /* check that we are not writing too much */
00331         uint32_t aligned_size = 0;
00332         if (pal_blockdevice_firmware_size < offset + buffer->size) {
00333             UC_PAAL_ERR_MSG("programming more than firmware size %" PRIu32
00334                             " < %" PRIu32 " + %" PRIu32,
00335                             pal_blockdevice_firmware_size, offset, buffer->size);
00336         } else if ((pal_blockdevice_firmware_size > offset + buffer->size) &&
00337                    (buffer->size % pal_blockdevice_page_size != 0)) {
00338             UC_PAAL_ERR_MSG("program size %" PRIu32 " does not align to page size %" PRIu32,
00339                             buffer->size, pal_blockdevice_page_size);
00340         } else if (pal_blockdevice_firmware_size == offset + buffer->size) {
00341             /* last chunk write page aligned data first */
00342             aligned_size = pal_blockdevice_round_down_to_page(buffer->size);
00343         } else {
00344             aligned_size = buffer->size;
00345         }
00346 
00347         /* aligned write */
00348         if (result.error == ERR_NONE && aligned_size > 0) {
00349             status = arm_uc_blockdevice_program(buffer->ptr,
00350                                                 physical_address,
00351                                                 aligned_size);
00352             if (status == ARM_UC_BLOCKDEVICE_FAIL) {
00353                 UC_PAAL_ERR_MSG("arm_uc_blockdevice_program failed");
00354             }
00355         }
00356 
00357         /* last chunk write remainder */
00358         uint32_t remainder_size = buffer->size - aligned_size;
00359 
00360         if ((status == ARM_UC_BLOCKDEVICE_SUCCESS) && (remainder_size > 0)) {
00361             /* check if it is safe to use buffer, i.e. buffer is larger than a page */
00362             if (buffer->size_max >= pal_blockdevice_page_size) {
00363                 memmove(buffer->ptr, &(buffer->ptr[aligned_size]), remainder_size);
00364                 status = arm_uc_blockdevice_program(buffer->ptr,
00365                                                     physical_address + aligned_size,
00366                                                     pal_blockdevice_page_size);
00367             } else {
00368                 UC_PAAL_ERR_MSG("arm_uc_blockdevice_program failed");
00369 
00370                 status = ARM_UC_BLOCKDEVICE_FAIL;
00371             }
00372         }
00373 
00374         if (status == ARM_UC_BLOCKDEVICE_SUCCESS) {
00375             /* set return code */
00376             result.code = ERR_NONE;
00377 
00378             /* signal done */
00379             pal_blockdevice_signal_internal(ARM_UC_PAAL_EVENT_WRITE_DONE);
00380         } else {
00381             UC_PAAL_ERR_MSG("arm_uc_blockdevice_program failed");
00382         }
00383     }
00384 
00385     return result;
00386 }
00387 
00388 /**
00389  * @brief Close storage location for writing and flush pending data.
00390  *
00391  * @param slot_id Storage location ID.
00392  * @return Returns ERR_NONE on accept, and signals the event handler with
00393  *         either DONE or ERROR when complete.
00394  *         Returns ERR_INVALID_PARAMETER on reject, and no signal is sent.
00395  */
00396 arm_uc_error_t ARM_UC_PAL_BlockDevice_Finalize(uint32_t slot_id)
00397 {
00398     arm_uc_error_t result = { .code = ERR_NONE };
00399 
00400     UC_PAAL_TRACE("ARM_UC_PAL_BlockDevice_Finalize");
00401 
00402     pal_blockdevice_signal_internal(ARM_UC_PAAL_EVENT_FINALIZE_DONE);
00403 
00404     return result;
00405 }
00406 
00407 /**
00408  * @brief Read a fragment from the indicated storage location.
00409  * @details The function will read until the buffer is full or the end of
00410  *          the storage location has been reached. The actual amount of
00411  *          bytes read is set in the buffer struct.
00412  *
00413  * @param slot_id Storage location ID.
00414  * @param offset Offset in bytes to read from.
00415  * @param buffer Pointer to buffer struct to store fragment. buffer->size
00416  *        contains the intended read size.
00417  * @return Returns ERR_NONE on accept, and signals the event handler with
00418  *         either DONE or ERROR when complete.
00419  *         Returns ERR_INVALID_PARAMETER on reject, and no signal is sent.
00420  *         buffer->size contains actual bytes read on return.
00421  */
00422 arm_uc_error_t ARM_UC_PAL_BlockDevice_Read(uint32_t slot_id,
00423                                            uint32_t offset,
00424                                            arm_uc_buffer_t *buffer)
00425 {
00426     arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER };
00427 
00428     if (buffer && buffer->ptr) {
00429         UC_PAAL_TRACE("ARM_UC_PAL_BlockDevice_Read: %" PRIX32 " %" PRIX32 " %" PRIX32,
00430                       slot_id, offset, buffer->size);
00431 
00432         /* find address of slot */
00433         uint32_t slot_addr = ARM_UC_BLOCKDEVICE_INVALID_SIZE;
00434         uint32_t slot_size = ARM_UC_BLOCKDEVICE_INVALID_SIZE;
00435         result = pal_blockdevice_get_slot_addr_size(slot_id,
00436                                                     &slot_addr,
00437                                                     &slot_size);
00438         uint32_t physical_address = slot_addr + pal_blockdevice_hdr_size + offset;
00439         uint32_t read_size = pal_blockdevice_round_up_to_page(buffer->size);
00440         int32_t status = ARM_UC_BLOCKDEVICE_FAIL;
00441 
00442         if (read_size <= buffer->size_max) {
00443             status = arm_uc_blockdevice_read(buffer->ptr,
00444                                              physical_address,
00445                                              read_size);
00446         }
00447 
00448         if (status == ARM_UC_BLOCKDEVICE_SUCCESS) {
00449             /* set return code */
00450             result.code = ERR_NONE;
00451 
00452             /* signal done */
00453             pal_blockdevice_signal_internal(ARM_UC_PAAL_EVENT_READ_DONE);
00454         } else {
00455             UC_PAAL_ERR_MSG("arm_uc_blockdevice_read failed");
00456         }
00457     }
00458 
00459     return result;
00460 }
00461 
00462 /**
00463  * @brief Set the firmware image in the slot to be the new active image.
00464  * @details This call is responsible for initiating the process for
00465  *          applying a new/different image. Depending on the platform this
00466  *          could be:
00467  *           * An empty call, if the installer can deduce which slot to
00468  *             choose from based on the firmware details.
00469  *           * Setting a flag to indicate which slot to use next.
00470  *           * Decompressing/decrypting/installing the firmware image on
00471  *             top of another.
00472  *
00473  * @param slot_id Storage location ID.
00474  * @return Returns ERR_NONE on accept, and signals the event handler with
00475  *         either DONE or ERROR when complete.
00476  *         Returns ERR_INVALID_PARAMETER on reject, and no signal is sent.
00477  */
00478 arm_uc_error_t ARM_UC_PAL_BlockDevice_Activate(uint32_t slot_id)
00479 {
00480     arm_uc_error_t result = { .code = ERR_NONE };
00481 
00482     UC_PAAL_TRACE("ARM_UC_PAL_BlockDevice_Activate");
00483 
00484     pal_blockdevice_signal_internal(ARM_UC_PAAL_EVENT_ACTIVATE_DONE);
00485 
00486     return result;
00487 }
00488 
00489 /**
00490  * @brief Get firmware details for the firmware image in the slot passed.
00491  * @details This call populates the passed details struct with information
00492  *          about the firmware image in the slot passed. Only the fields
00493  *          marked as supported in the capabilities bitmap will have valid
00494  *          values.
00495  *
00496  * @param slot_id Storage location ID.
00497  * @param details Pointer to firmware details struct to be populated.
00498  * @return Returns ERR_NONE on accept, and signals the event handler with
00499  *         either DONE or ERROR when complete.
00500  *         Returns ERR_INVALID_PARAMETER on reject, and no signal is sent.
00501  */
00502 arm_uc_error_t ARM_UC_PAL_BlockDevice_GetFirmwareDetails(
00503     uint32_t slot_id,
00504     arm_uc_firmware_details_t *details)
00505 {
00506     arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER };
00507 
00508     if (details) {
00509         UC_PAAL_TRACE("ARM_UC_PAL_BlockDevice_GetFirmwareDetails");
00510 
00511         /* find address of slot */
00512         uint32_t slot_addr = ARM_UC_BLOCKDEVICE_INVALID_SIZE;
00513         uint32_t slot_size = ARM_UC_BLOCKDEVICE_INVALID_SIZE;
00514         result = pal_blockdevice_get_slot_addr_size(slot_id, &slot_addr, &slot_size);
00515         uint8_t buffer[pal_blockdevice_hdr_size];
00516 
00517         int status = arm_uc_blockdevice_read(buffer,
00518                                              slot_addr,
00519                                              pal_blockdevice_hdr_size);
00520 
00521         if (status == ARM_UC_BLOCKDEVICE_SUCCESS) {
00522             result = arm_uc_parse_external_header_v2(buffer, details);
00523 
00524             if (result.error == ERR_NONE) {
00525                 /* signal done */
00526                 pal_blockdevice_signal_internal(ARM_UC_PAAL_EVENT_GET_FIRMWARE_DETAILS_DONE);
00527             } else {
00528                 UC_PAAL_ERR_MSG("arm_uc_parse_external_header_v2 failed");
00529             }
00530         } else {
00531             UC_PAAL_ERR_MSG("arm_uc_blockdevice_read failed");
00532         }
00533     }
00534 
00535     return result;
00536 }
00537 
00538 #endif // #if defined(ARM_UC_FEATURE_PAL_BLOCKDEVICE)