Mayank Gupta / Mbed OS pelion-example-frdm

Dependencies:   FXAS21002 FXOS8700Q

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers arm_uc_pal_linux_implementation.c Source File

arm_uc_pal_linux_implementation.c

00001 // ----------------------------------------------------------------------------
00002 // Copyright 2016-2017 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_LINUX) && (ARM_UC_FEATURE_PAL_LINUX == 1)
00021 #if defined(TARGET_IS_PC_LINUX)
00022 #define _FILE_OFFSET_BITS  64
00023 
00024 #include "update-client-pal-linux/arm_uc_pal_linux_implementation_internal.h"
00025 #include "update-client-pal-linux/arm_uc_pal_linux_implementation.h"
00026 #include "update-client-paal/arm_uc_paal_update_api.h"
00027 #include "update-client-pal-linux/arm_uc_pal_linux_ext.h"
00028 
00029 #include "update-client-metadata-header/arm_uc_metadata_header_v2.h"
00030 
00031 #define __STDC_FORMAT_MACROS
00032 #include <inttypes.h>
00033 #include <stdio.h>
00034 #include <errno.h>
00035 #include <pthread.h>
00036 #include <stdlib.h>
00037 #include <sys/stat.h>
00038 #include <sys/wait.h>
00039 
00040 extern linux_worker_thread_info_t linux_worker_thread;
00041 
00042 /* worker struct, must be accessible externally */
00043 arm_ucp_worker_config_t arm_uc_worker_parameters = { 0 };
00044 
00045 static FILE *arm_uc_firmware_descriptor = NULL;
00046 
00047 static arm_uc_error_t spawn_thread(void *(*start_routine)(void *), void *arg)
00048 {
00049     arm_uc_error_t result = {ERR_NONE};
00050 
00051     /* Get the thread mutex. This prevents another thread from being spawned until this one completes.
00052        There should only ever be one thread at a time, since they are spawned via a single-threaded
00053        state machine, but this guarantees that there will only be one. */
00054     int status = pthread_mutex_trylock(&linux_worker_thread.mutex);
00055     if (status == EBUSY) {
00056         ARM_UC_SET_ERROR(result, ERR_NOT_READY);
00057     } else if (status != 0) {
00058         uint32_t code = (TWO_CC('P', 'T') << 16) | (status & 0xFFFF);
00059         ARM_UC_SET_ERROR(result, code);
00060     }
00061     /* Create "detached thread" attribute only once */
00062     if (result.error == ERR_NONE && linux_worker_thread.attr_initialized == 0) {
00063         if ((status = pthread_attr_init(&linux_worker_thread.attr)) != 0) {
00064             result.error = ERR_INVALID_PARAMETER;
00065             pthread_mutex_unlock(&linux_worker_thread.mutex);
00066         } else {
00067             if ((status = pthread_attr_setdetachstate(&linux_worker_thread.attr, PTHREAD_CREATE_DETACHED)) != 0) {
00068                 result.error = ERR_INVALID_PARAMETER;
00069                 pthread_mutex_unlock(&linux_worker_thread.mutex);
00070             } else {
00071                 linux_worker_thread.attr_initialized = 1;
00072             }
00073         }
00074     }
00075     if (result.error == ERR_NONE) {
00076         /* create a detached thread to execute the supplied routine */
00077         int status = pthread_create(&linux_worker_thread.thread,
00078                                     &linux_worker_thread.attr,
00079                                     start_routine,
00080                                     arg);
00081 
00082         /* check if thread was created successfully */
00083         if (status != 0) {
00084             result.code = ERR_INVALID_PARAMETER;
00085             pthread_mutex_unlock(&linux_worker_thread.mutex);
00086         }
00087     }
00088     return result;
00089 }
00090 
00091 /**
00092  * @brief Initialize the underlying storage and set the callback handler.
00093  *
00094  * @param callback Function pointer to event handler.
00095  * @return Returns ERR_NONE on accept, and signals the event handler with
00096  *         either DONE or ERROR when complete.
00097  *         Returns ERR_INVALID_PARAMETER on reject, and no signal is sent.
00098  */
00099 arm_uc_error_t ARM_UC_PAL_Linux_Initialize(ARM_UC_PAAL_UPDATE_SignalEvent_t callback)
00100 {
00101     arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER };
00102 
00103     if (callback) {
00104         arm_uc_pal_linux_internal_set_callback(callback);
00105 
00106         /* create folder for headers if it does not already exist */
00107         errno = 0;
00108         int status = mkdir(ARM_UC_HEADER_FOLDER_PATH, 0700);
00109 
00110         if ((status == 0) || (errno == EEXIST)) {
00111             /* create folder for firmwares if it does not already exist */
00112             errno = 0;
00113             status = mkdir(ARM_UC_FIRMWARE_FOLDER_PATH, 0700);
00114 
00115             if ((status == 0) || (errno == EEXIST)) {
00116                 /* set return code on success */
00117                 result.code = ERR_NONE;
00118             }
00119         }
00120 
00121         /* signal completion or perform extended preparation */
00122         if (result.error == ERR_NONE) {
00123             /* set explicit ERR_NONE upon success */
00124             result.code = ERR_NONE;
00125 
00126             if (arm_uc_worker_parameters.initialize) {
00127                 /* use extended prepare, invoke script from worker thread */
00128 
00129                 /* create a second thread which executes worker_parameters_prepare */
00130                 result = spawn_thread(arm_uc_pal_linux_extended_post_worker,
00131                                       arm_uc_worker_parameters.initialize);
00132             } else {
00133                 /* call event handler */
00134                 arm_uc_pal_linux_signal_callback(ARM_UC_PAAL_EVENT_INITIALIZE_DONE, false);
00135             }
00136         }
00137 
00138     }
00139 
00140     return result;
00141 }
00142 
00143 /**
00144  * @brief Get maximum number of supported storage locations.
00145  *
00146  * @return Number of storage locations.
00147  */
00148 uint32_t ARM_UC_PAL_Linux_GetMaxID(void)
00149 {
00150     return 1;
00151 }
00152 
00153 /**
00154  * @brief Prepare the storage layer for a new firmware image.
00155  * @details The storage location is set up to receive an image with
00156  *          the details passed in the details struct.
00157  *
00158  * @param location Storage location ID.
00159  * @param details Pointer to a struct with firmware details.
00160  * @param buffer Temporary buffer for formatting and storing metadata.
00161  * @return Returns ERR_NONE on accept, and signals the event handler with
00162  *         either DONE or ERROR when complete.
00163  *         Returns ERR_INVALID_PARAMETER on reject, and no signal is sent.
00164  */
00165 arm_uc_error_t ARM_UC_PAL_Linux_Prepare(uint32_t location,
00166                                         const arm_uc_firmware_details_t *details,
00167                                         arm_uc_buffer_t *buffer)
00168 {
00169     arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER };
00170 
00171     if (details && buffer) {
00172         UC_PAAL_TRACE("details size: %" PRIu64, details->size);
00173 
00174         /* write header */
00175         result = arm_uc_pal_linux_internal_write_header(&location, details);
00176 
00177         /* allocate space for firmware */
00178         if (result.error == ERR_NONE) {
00179             char file_path[ARM_UC_MAXIMUM_FILE_AND_PATH_LENGTH] = { 0 };
00180 
00181             /* construct header file path */
00182             result = arm_uc_pal_linux_internal_file_path(file_path,
00183                                                          ARM_UC_MAXIMUM_FILE_AND_PATH_LENGTH,
00184                                                          ARM_UC_FIRMWARE_FOLDER_PATH,
00185                                                          "firmware",
00186                                                          &location);
00187 
00188             UC_PAAL_TRACE("file path: %s", file_path);
00189 
00190             if (result.error == ERR_NONE) {
00191                 /* open file */
00192                 errno = 0;
00193                 FILE *descriptor = fopen(file_path, "wb");
00194 
00195                 if (descriptor != NULL) {
00196                     /* allocate space by writing empty file */
00197                     memset(buffer->ptr, 0, buffer->size_max);
00198                     buffer->size = buffer->size_max;
00199 
00200                     uint64_t index = 0;
00201 /*
00202  * writing of full file is not enabled by default now. Few drawbacks of writing before download:
00203  * -kernel watchdog issue due some file IO problem with a huge write (around 4GB)
00204  * -wear of the MCC device for unnecessary physical writes (there is max number of writes that can be done before physical cells of memory break)
00205  * -increased time of update process. specially with huge files this will be many minutes of extra time spend to MCC write.
00206 */
00207 #ifdef WRITE_FULL_PHYSICAL_FILE
00208                     uint64_t writeSize = details->size;
00209 #else
00210                     uint64_t writeSize = 1;
00211 #endif
00212                     while (index < writeSize) {
00213                         // calculate write size to handle overspill
00214                         size_t actual_size = details->size - index;
00215 
00216                         if (actual_size > buffer->size) {
00217                             actual_size = buffer->size;
00218                         }
00219 
00220                         /* write buffer */
00221                         size_t xfer_size = fwrite(buffer->ptr,
00222                                                   sizeof(uint8_t),
00223                                                   actual_size,
00224                                                   descriptor);
00225 
00226                         /* break out if write failed */
00227                         if (xfer_size == actual_size) {
00228                             index += actual_size;
00229                         } else {
00230                             result.code = PAAL_ERR_FIRMWARE_TOO_LARGE;
00231                             break;
00232                         }
00233                     }
00234 
00235                     /* close file after write */
00236                     int status = fclose(descriptor);
00237 
00238                     if (status == EOF) {
00239                         UC_PAAL_ERR_MSG("failed to allocate space for firmware");
00240                         result.code = ERR_INVALID_PARAMETER;
00241                     }
00242                 } else {
00243                     UC_PAAL_ERR_MSG("failed to open file: %s", strerror(errno));
00244                 }
00245             } else {
00246                 UC_PAAL_ERR_MSG("file name and path too long");
00247             }
00248         } else {
00249             UC_PAAL_ERR_MSG("could not write header");
00250         }
00251 
00252         /* signal completion or perform extended preparation */
00253         if (result.error == ERR_NONE) {
00254             /* set explicit ERR_NONE upon success */
00255             result.code = ERR_NONE;
00256 
00257             if (arm_uc_worker_parameters.prepare) {
00258                 /* use extended prepare, invoke script from worker thread */
00259                 /* export location */
00260                 arm_uc_pal_linux_internal_set_location(&location);
00261 
00262                 /* create a second thread which executes worker_parameters_prepare */
00263                 result = spawn_thread(arm_uc_pal_linux_extended_post_worker,
00264                                       arm_uc_worker_parameters.prepare);
00265             } else {
00266                 /* call event handler */
00267                 arm_uc_pal_linux_signal_callback(ARM_UC_PAAL_EVENT_PREPARE_DONE, false);
00268             }
00269         }
00270     }
00271 
00272     return result;
00273 }
00274 
00275 /**
00276  * @brief Write a fragment to the indicated storage location.
00277  * @details The storage location must have been allocated using the Prepare
00278  *          call. The call is expected to write the entire fragment before
00279  *          signaling completion.
00280  *
00281  * @param location Storage location ID.
00282  * @param offset Offset in bytes to where the fragment should be written.
00283  * @param buffer Pointer to buffer struct with fragment.
00284  * @return Returns ERR_NONE on accept, and signals the event handler with
00285  *         either DONE or ERROR when complete.
00286  *         Returns ERR_INVALID_PARAMETER on reject, and no signal is sent.
00287  */
00288 arm_uc_error_t ARM_UC_PAL_Linux_Write(uint32_t location,
00289                                       uint32_t offset,
00290                                       const arm_uc_buffer_t *buffer)
00291 {
00292     arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER };
00293 
00294     if (buffer) {
00295         /* reverse default error code */
00296         result.code = ERR_NONE;
00297 
00298         /* open file if descriptor is not set */
00299         if (arm_uc_firmware_descriptor == NULL) {
00300             char file_path[ARM_UC_MAXIMUM_FILE_AND_PATH_LENGTH] = { 0 };
00301 
00302             /* construct firmware file path */
00303             result = arm_uc_pal_linux_internal_file_path(file_path,
00304                                                          ARM_UC_MAXIMUM_FILE_AND_PATH_LENGTH,
00305                                                          ARM_UC_FIRMWARE_FOLDER_PATH,
00306                                                          "firmware",
00307                                                          &location);
00308 
00309             if (result.error == ERR_NONE) {
00310                 /* open file */
00311                 if (arm_uc_worker_parameters.write) {
00312                     /* in extended write, each fragment is stored in its own file */
00313                     arm_uc_firmware_descriptor = fopen(file_path, "w+b");
00314 
00315                     /* export offset before resetting it */
00316                     arm_uc_pal_linux_internal_set_offset(offset);
00317                     offset = 0;
00318                 } else {
00319                     /* in normal write, each fragment is added to an existing file */
00320                     arm_uc_firmware_descriptor = fopen(file_path, "r+b");
00321                 }
00322             } else {
00323                 UC_PAAL_ERR_MSG("firmware file name and path too long");
00324             }
00325         }
00326 
00327         /* continue if file is open */
00328         if (arm_uc_firmware_descriptor != NULL) {
00329             /* set write position */
00330             int status = fseeko(arm_uc_firmware_descriptor,
00331                                 offset,
00332                                 SEEK_SET);
00333 
00334             /* continue if position is set */
00335             if (status == 0) {
00336                 /* write buffer */
00337                 size_t xfer_size = fwrite(buffer->ptr,
00338                                           sizeof(uint8_t),
00339                                           buffer->size,
00340                                           arm_uc_firmware_descriptor);
00341 
00342                 /* set error code if write failed */
00343                 if (xfer_size != buffer->size) {
00344                     UC_PAAL_ERR_MSG("failed to write firmware");
00345                     result.code = ERR_INVALID_PARAMETER;
00346                 }
00347 
00348                 /* if using extended write */
00349                 if (arm_uc_worker_parameters.write) {
00350                     /* close file after write */
00351                     int status = fclose(arm_uc_firmware_descriptor);
00352                     arm_uc_firmware_descriptor = NULL;
00353 
00354                     if (status == EOF) {
00355                         UC_PAAL_ERR_MSG("failed to close firmware file");
00356                         result.code = ERR_INVALID_PARAMETER;
00357                     }
00358                 }
00359             } else {
00360                 UC_PAAL_ERR_MSG("failed to seek in firmware");
00361                 result.code = ERR_INVALID_PARAMETER;
00362             }
00363         }
00364 
00365         /* signal completion or perform extended write */
00366         if (result.error == ERR_NONE) {
00367             /* set explicit ERR_NONE */
00368             result.code = ERR_NONE;
00369 
00370             if (arm_uc_worker_parameters.write) {
00371                 /* use extended write, invoke script from worker thread */
00372                 /* export location */
00373                 arm_uc_pal_linux_internal_set_location(&location);
00374 
00375                 /* create a second thread which executes worker_parameters_write */
00376                 result = spawn_thread(arm_uc_pal_linux_extended_post_worker,
00377                                       arm_uc_worker_parameters.write);
00378             } else {
00379                 /* call event handler */
00380                 arm_uc_pal_linux_signal_callback(ARM_UC_PAAL_EVENT_WRITE_DONE, false);
00381             }
00382         }
00383     }
00384 
00385     return result;
00386 }
00387 
00388 /**
00389  * @brief Close storage location for writing and flush pending data.
00390  *
00391  * @param location 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_Linux_Finalize(uint32_t location)
00397 {
00398     arm_uc_error_t result = { .code = ERR_NONE };
00399 
00400     /* only close firmware file if descriptor is not NULL */
00401     if (arm_uc_firmware_descriptor != NULL) {
00402         /* close file */
00403         int status = fclose(arm_uc_firmware_descriptor);
00404         arm_uc_firmware_descriptor = NULL;
00405 
00406         if (status == EOF) {
00407             UC_PAAL_ERR_MSG("failed to close firmware file");
00408             result.code = ERR_INVALID_PARAMETER;
00409         }
00410     }
00411 
00412     /* signal completion or perform extended finalization */
00413     if (result.error == ERR_NONE) {
00414         /* explicitly set code to ERR_NONE */
00415         result.code = ERR_NONE;
00416 
00417         /* use extended finalize, invoke script from worker thread */
00418         if (arm_uc_worker_parameters.finalize) {
00419             /* export location */
00420             arm_uc_pal_linux_internal_set_location(&location);
00421 
00422             /* create a second thread which executes worker_parameters_finalize */
00423             result = spawn_thread(arm_uc_pal_linux_extended_post_worker,
00424                                   arm_uc_worker_parameters.finalize);
00425         } else {
00426             /* call event handler */
00427             arm_uc_pal_linux_signal_callback(ARM_UC_PAAL_EVENT_FINALIZE_DONE, false);
00428         }
00429     }
00430 
00431     return result;
00432 }
00433 
00434 /**
00435  * @brief Read a fragment from the indicated storage location.
00436  * @details The function will read until the buffer is full or the end of
00437  *          the storage location has been reached. The actual amount of
00438  *          bytes read is set in the buffer struct.
00439  *
00440  * @param location Storage location ID.
00441  * @param offset Offset in bytes to read from.
00442  * @param buffer Pointer to buffer struct to store fragment. buffer->size
00443  *        contains the intended read size.
00444  * @return Returns ERR_NONE on accept, and signals the event handler with
00445  *         either DONE or ERROR when complete.
00446  *         Returns ERR_INVALID_PARAMETER on reject, and no signal is sent.
00447  *         buffer->size contains actual bytes read on return.
00448  */
00449 arm_uc_error_t ARM_UC_PAL_Linux_Read(uint32_t location,
00450                                      uint32_t offset,
00451                                      arm_uc_buffer_t *buffer)
00452 {
00453     arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER };
00454 
00455     if (buffer) {
00456         /* use extended finalize, invoke script from worker thread */
00457         if (arm_uc_worker_parameters.read) {
00458             /* export location, offset and buffer */
00459             arm_uc_pal_linux_internal_set_location(&location);
00460             arm_uc_pal_linux_internal_set_offset(offset);
00461             arm_uc_pal_linux_internal_set_buffer(buffer);
00462 
00463             /* create a second thread which executes worker_parameters_read */
00464             result = spawn_thread(arm_uc_pal_linux_extended_pre_worker,
00465                                   arm_uc_worker_parameters.read);
00466         } else {
00467             /* normal read */
00468             char file_path[ARM_UC_MAXIMUM_FILE_AND_PATH_LENGTH] = { 0 };
00469 
00470             /* construct firmware file path */
00471             result = arm_uc_pal_linux_internal_file_path(file_path,
00472                                                          ARM_UC_MAXIMUM_FILE_AND_PATH_LENGTH,
00473                                                          ARM_UC_FIRMWARE_FOLDER_PATH,
00474                                                          "firmware",
00475                                                          &location);
00476 
00477             /* file path is valid */
00478             if (result.error == ERR_NONE) {
00479                 result = arm_uc_pal_linux_internal_read(file_path, offset, buffer);
00480 
00481                 /* signal completion */
00482                 if (result.error == ERR_NONE) {
00483                     /* call event handler */
00484                     arm_uc_pal_linux_signal_callback(ARM_UC_PAAL_EVENT_READ_DONE, false);
00485                 }
00486             }
00487         }
00488     }
00489 
00490     return result;
00491 }
00492 
00493 /**
00494  * @brief Set the firmware image in the slot to be the new active image.
00495  * @details This call is responsible for initiating the process for
00496  *          applying a new/different image. Depending on the platform this
00497  *          could be:
00498  *           * An empty call, if the installer can deduce which slot to
00499  *             choose from based on the firmware details.
00500  *           * Setting a flag to indicate which slot to use next.
00501  *           * Decompressing/decrypting/installing the firmware image on
00502  *             top of another.
00503  *
00504  * @param location Storage location ID.
00505  * @return Returns ERR_NONE on accept, and signals the event handler with
00506  *         either DONE or ERROR when complete.
00507  *         Returns ERR_INVALID_PARAMETER on reject, and no signal is sent.
00508  */
00509 arm_uc_error_t ARM_UC_PAL_Linux_Activate(uint32_t location)
00510 {
00511     arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER };
00512 
00513     /* read firmware details from location */
00514     arm_uc_firmware_details_t details = { 0 };
00515     result = arm_uc_pal_linux_internal_read_header(&location, &details);
00516 
00517     if (result.error == ERR_NONE) {
00518         UC_PAAL_TRACE("version: %" PRIu64, details.version);
00519         UC_PAAL_TRACE("size: %"PRIu64, details.size);
00520 
00521         /* write details to active location */
00522         result = arm_uc_pal_linux_internal_write_header(NULL, &details);
00523 
00524         if (result.error == ERR_NONE) {
00525             /* explicitly set code to ERR_NONE */
00526             result.code = ERR_NONE;
00527 
00528             /* use extended activate, invoke script from worker thread */
00529             if (arm_uc_worker_parameters.activate) {
00530                 /* export location */
00531                 arm_uc_pal_linux_internal_set_location(&location);
00532 
00533                 /* create a second thread which executes worker_parameters_read */
00534                 result = spawn_thread(arm_uc_pal_linux_extended_post_worker,
00535                                       arm_uc_worker_parameters.activate);
00536             } else {
00537                 arm_uc_pal_linux_signal_callback(ARM_UC_PAAL_EVENT_ACTIVATE_DONE, false);
00538             }
00539         }
00540     }
00541 
00542     return result;
00543 }
00544 
00545 /**
00546  * @brief Get firmware details for the actively running firmware.
00547  * @details This call populates the passed details struct with information
00548  *          about the currently active firmware image. Only the fields
00549  *          marked as supported in the capabilities bitmap will have valid
00550  *          values.
00551  *
00552  * @param details Pointer to firmware details struct to be populated.
00553  * @return Returns ERR_NONE on accept, and signals the event handler with
00554  *         either DONE or ERROR when complete.
00555  *         Returns ERR_INVALID_PARAMETER on reject, and no signal is sent.
00556  */
00557 arm_uc_error_t ARM_UC_PAL_Linux_GetActiveFirmwareDetails(arm_uc_firmware_details_t *details)
00558 {
00559     arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER };
00560 
00561     if (details) {
00562         /* use extended get firmware details, invoke script from worker thread */
00563         if (arm_uc_worker_parameters.active_details) {
00564             /* export details */
00565             arm_uc_pal_linux_internal_set_details(details);
00566 
00567             /* create a second thread which executes worker_parameters_read */
00568             result = spawn_thread(arm_uc_pal_linux_extended_pre_worker,
00569                                   arm_uc_worker_parameters.active_details);
00570 
00571         } else {
00572             /* normal read */
00573             result = arm_uc_pal_linux_internal_read_header(NULL, details);
00574 
00575             if (result.error == ERR_NONE) {
00576                 UC_PAAL_TRACE("version: %" PRIu64, details->version);
00577                 UC_PAAL_TRACE("size: %"PRIu64, details->size);
00578 
00579                 if (result.error == ERR_NONE) {
00580                     arm_uc_pal_linux_signal_callback(ARM_UC_PAAL_EVENT_GET_ACTIVE_FIRMWARE_DETAILS_DONE, false);
00581                 }
00582             }
00583         }
00584     }
00585 
00586     return result;
00587 }
00588 
00589 /**
00590  * @brief Get firmware details for the firmware image in the slot passed.
00591  * @details This call populates the passed details struct with information
00592  *          about the firmware image in the slot passed. Only the fields
00593  *          marked as supported in the capabilities bitmap will have valid
00594  *          values.
00595  *
00596  * @param details Pointer to firmware details struct to be populated.
00597  * @return Returns ERR_NONE on accept, and signals the event handler with
00598  *         either DONE or ERROR when complete.
00599  *         Returns ERR_INVALID_PARAMETER on reject, and no signal is sent.
00600  */
00601 arm_uc_error_t ARM_UC_PAL_Linux_GetFirmwareDetails(uint32_t location,
00602                                                    arm_uc_firmware_details_t *details)
00603 {
00604     arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER };
00605 
00606     if (details) {
00607         /* use extended get firmware details, invoke script from worker thread */
00608         if (arm_uc_worker_parameters.details) {
00609             /* export location and details */
00610             arm_uc_pal_linux_internal_set_location(&location);
00611             arm_uc_pal_linux_internal_set_details(details);
00612 
00613             /* create a second thread which executes worker_parameters_read */
00614             result = spawn_thread(arm_uc_pal_linux_extended_pre_worker,
00615                                   arm_uc_worker_parameters.details);
00616         } else {
00617             /* normal read */
00618             result = arm_uc_pal_linux_internal_read_header(&location, details);
00619 
00620             if (result.error == ERR_NONE) {
00621                 UC_PAAL_TRACE("version: %" PRIu64, details->version);
00622                 UC_PAAL_TRACE("size: %"PRIu64, details->size);
00623 
00624                 arm_uc_pal_linux_signal_callback(ARM_UC_PAAL_EVENT_GET_FIRMWARE_DETAILS_DONE, false);
00625             }
00626         }
00627     }
00628 
00629     return result;
00630 }
00631 
00632 /**
00633  * @brief Get details for the component responsible for installation.
00634  * @details This call populates the passed details struct with information
00635  *          about the local installer. Only the fields marked as supported
00636  *          in the capabilities bitmap will have valid values. The
00637  *          installer could be the bootloader, a recovery image, or some
00638  *          other component responsible for applying the new firmware
00639  *          image.
00640  *
00641  * @param details Pointer to installer details struct to be populated.
00642  * @return Returns ERR_NONE on accept, and signals the event handler with
00643  *         either DONE or ERROR when complete.
00644  *         Returns ERR_INVALID_PARAMETER on reject, and no signal is sent.
00645  */
00646 arm_uc_error_t ARM_UC_PAL_Linux_GetInstallerDetails(arm_uc_installer_details_t *details)
00647 {
00648     arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER };
00649 
00650     if (details) {
00651         /* use extended installer details, invoke script from worker thread */
00652         if (arm_uc_worker_parameters.installer) {
00653             /* export installer details */
00654             arm_uc_pal_linux_internal_set_installer(details);
00655 
00656             /* create a second thread which executes worker_parameters_read */
00657             result = spawn_thread(arm_uc_pal_linux_extended_pre_worker,
00658                                   arm_uc_worker_parameters.installer);
00659         } else {
00660             /* normal read */
00661             result = arm_uc_pal_linux_internal_read_installer(details);
00662 
00663             if (result.error == ERR_NONE) {
00664                 arm_uc_pal_linux_signal_callback(ARM_UC_PAAL_EVENT_GET_INSTALLER_DETAILS_DONE, false);
00665             }
00666         }
00667     }
00668 
00669     return result;
00670 }
00671 
00672 /**
00673  * @brief Write a manifest to a file.
00674  * @param location Storage location ID.
00675  * @param buffer Buffer that contains the manifest.
00676  * @return Returns ERR_NONE if the manifest was written.
00677  *         Returns ERR_INVALID_PARAMETER on error.
00678  */
00679 arm_uc_error_t ARM_UC_PAL_Linux_WriteManifest(uint32_t location, const arm_uc_buffer_t *buffer)
00680 {
00681     return arm_uc_pal_linux_internal_write_manifest(&location, buffer);
00682 }
00683 
00684 #endif /* TARGET_IS_PC_LINUX */
00685 #endif /* ARM_UC_FEATURE_PAL_LINUX */