summaryrefslogtreecommitdiff
path: root/bl1/bl1_fwu.c
diff options
context:
space:
mode:
Diffstat (limited to 'bl1/bl1_fwu.c')
-rw-r--r--bl1/bl1_fwu.c210
1 files changed, 210 insertions, 0 deletions
diff --git a/bl1/bl1_fwu.c b/bl1/bl1_fwu.c
index ace364d4..205ea922 100644
--- a/bl1/bl1_fwu.c
+++ b/bl1/bl1_fwu.c
@@ -40,6 +40,8 @@ static register_t bl1_fwu_image_resume(register_t image_param,
unsigned int flags);
static int bl1_fwu_sec_image_done(void **handle,
unsigned int flags);
+static int bl1_fwu_image_reset(unsigned int image_id,
+ unsigned int flags);
__dead2 static void bl1_fwu_done(void *client_cookie, void *reserved);
/*
@@ -47,6 +49,9 @@ __dead2 static void bl1_fwu_done(void *client_cookie, void *reserved);
*/
static unsigned int sec_exec_image_id = INVALID_IMAGE_ID;
+/* Authentication status of each image. */
+extern unsigned int auth_img_flags[];
+
void cm_set_next_context(void *cpu_context);
/*******************************************************************************
@@ -78,6 +83,9 @@ register_t bl1_fwu_smc_handler(unsigned int smc_fid,
case FWU_SMC_SEC_IMAGE_DONE:
SMC_RET1(handle, bl1_fwu_sec_image_done(&handle, flags));
+ case FWU_SMC_IMAGE_RESET:
+ SMC_RET1(handle, bl1_fwu_image_reset(x1, flags));
+
case FWU_SMC_UPDATE_DONE:
bl1_fwu_done((void *)x1, NULL);
/* We should never return from bl1_fwu_done() */
@@ -91,6 +99,124 @@ register_t bl1_fwu_smc_handler(unsigned int smc_fid,
}
/*******************************************************************************
+ * Utility functions to keep track of the images that are loaded at any time.
+ ******************************************************************************/
+
+#ifdef PLAT_FWU_MAX_SIMULTANEOUS_IMAGES
+#define FWU_MAX_SIMULTANEOUS_IMAGES PLAT_FWU_MAX_SIMULTANEOUS_IMAGES
+#else
+#define FWU_MAX_SIMULTANEOUS_IMAGES 10
+#endif
+
+static int bl1_fwu_loaded_ids[FWU_MAX_SIMULTANEOUS_IMAGES] = {
+ [0 ... FWU_MAX_SIMULTANEOUS_IMAGES-1] = INVALID_IMAGE_ID
+};
+
+/*
+ * Adds an image_id to the bl1_fwu_loaded_ids array.
+ * Returns 0 on success, 1 on error.
+ */
+static int bl1_fwu_add_loaded_id(int image_id)
+{
+ int i;
+
+ /* Check if the ID is already in the list */
+ for (i = 0; i < FWU_MAX_SIMULTANEOUS_IMAGES; i++) {
+ if (bl1_fwu_loaded_ids[i] == image_id)
+ return 0;
+ }
+
+ /* Find an empty slot */
+ for (i = 0; i < FWU_MAX_SIMULTANEOUS_IMAGES; i++) {
+ if (bl1_fwu_loaded_ids[i] == INVALID_IMAGE_ID) {
+ bl1_fwu_loaded_ids[i] = image_id;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Removes an image_id from the bl1_fwu_loaded_ids array.
+ * Returns 0 on success, 1 on error.
+ */
+static int bl1_fwu_remove_loaded_id(int image_id)
+{
+ int i;
+
+ /* Find the ID */
+ for (i = 0; i < FWU_MAX_SIMULTANEOUS_IMAGES; i++) {
+ if (bl1_fwu_loaded_ids[i] == image_id) {
+ bl1_fwu_loaded_ids[i] = INVALID_IMAGE_ID;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*******************************************************************************
+ * This function checks if the specified image overlaps another image already
+ * loaded. It returns 0 if there is no overlap, a negative error code otherwise.
+ ******************************************************************************/
+static int bl1_fwu_image_check_overlaps(int image_id)
+{
+ const image_desc_t *image_desc, *checked_image_desc;
+ const image_info_t *info, *checked_info;
+
+ uintptr_t image_base, image_end;
+ uintptr_t checked_image_base, checked_image_end;
+
+ checked_image_desc = bl1_plat_get_image_desc(image_id);
+ checked_info = &checked_image_desc->image_info;
+
+ /* Image being checked mustn't be empty. */
+ assert(checked_info->image_size != 0);
+
+ checked_image_base = checked_info->image_base;
+ checked_image_end = checked_image_base + checked_info->image_size - 1;
+ /* No need to check for overlaps, it's done in bl1_fwu_image_copy(). */
+
+ for (int i = 0; i < FWU_MAX_SIMULTANEOUS_IMAGES; i++) {
+
+ /* Don't check image against itself. */
+ if (bl1_fwu_loaded_ids[i] == image_id)
+ continue;
+
+ image_desc = bl1_plat_get_image_desc(bl1_fwu_loaded_ids[i]);
+
+ /* Only check images that are loaded or being loaded. */
+ assert (image_desc->state != IMAGE_STATE_RESET);
+
+ info = &image_desc->image_info;
+
+ /* There cannot be overlaps with an empty image. */
+ if (info->image_size == 0)
+ continue;
+
+ image_base = info->image_base;
+ image_end = image_base + info->image_size - 1;
+ /*
+ * Overflows cannot happen. It is checked in
+ * bl1_fwu_image_copy() when the image goes from RESET to
+ * COPYING or COPIED.
+ */
+ assert (image_end > image_base);
+
+ /* Check if there are overlaps. */
+ if (!(image_end < checked_image_base ||
+ checked_image_end < image_base)) {
+ VERBOSE("Image with ID %d overlaps existing image with ID %d",
+ checked_image_desc->image_id, image_desc->image_id);
+ return -EPERM;
+ }
+ }
+
+ return 0;
+}
+
+/*******************************************************************************
* This function is responsible for copying secure images in AP Secure RAM.
******************************************************************************/
static int bl1_fwu_image_copy(unsigned int image_id,
@@ -189,6 +315,13 @@ static int bl1_fwu_image_copy(unsigned int image_id,
/* Save the given image size. */
image_desc->image_info.image_size = image_size;
+ /* Make sure the image doesn't overlap other images. */
+ if (bl1_fwu_image_check_overlaps(image_id)) {
+ image_desc->image_info.image_size = 0;
+ WARN("BL1-FWU: This image overlaps another one\n");
+ return -EPERM;
+ }
+
/*
* copied_size must be explicitly initialized here because the
* FWU code doesn't necessarily do it when it resets the state
@@ -213,6 +346,11 @@ static int bl1_fwu_image_copy(unsigned int image_id,
return -ENOMEM;
}
+ if (bl1_fwu_add_loaded_id(image_id)) {
+ WARN("BL1-FWU: Too many images loaded at the same time.\n");
+ return -ENOMEM;
+ }
+
/* Everything looks sane. Go ahead and copy the block of data. */
dest_addr = image_desc->image_info.image_base + image_desc->copied_size;
memcpy((void *) dest_addr, (const void *) image_src, block_size);
@@ -290,6 +428,11 @@ static int bl1_fwu_image_auth(unsigned int image_id,
return -ENOMEM;
}
+ if (bl1_fwu_add_loaded_id(image_id)) {
+ WARN("BL1-FWU: Too many images loaded at the same time.\n");
+ return -ENOMEM;
+ }
+
base_addr = image_src;
total_size = image_size;
@@ -319,6 +462,13 @@ static int bl1_fwu_image_auth(unsigned int image_id,
/* Indicate that image can be copied again*/
image_desc->state = IMAGE_STATE_RESET;
}
+
+ /*
+ * Even if this fails it's ok because the ID isn't in the array.
+ * The image cannot be in RESET state here, it is checked at the
+ * beginning of the function.
+ */
+ bl1_fwu_remove_loaded_id(image_id);
return -EAUTH;
}
@@ -475,6 +625,13 @@ static int bl1_fwu_sec_image_done(void **handle, unsigned int flags)
assert(EP_GET_EXE(image_desc->ep_info.h.attr) == EXECUTABLE);
assert(image_desc->state == IMAGE_STATE_EXECUTED);
+#if ENABLE_ASSERTIONS
+ int rc = bl1_fwu_remove_loaded_id(sec_exec_image_id);
+ assert(rc == 0);
+#else
+ bl1_fwu_remove_loaded_id(sec_exec_image_id);
+#endif
+
/* Update the flags. */
image_desc->state = IMAGE_STATE_RESET;
sec_exec_image_id = INVALID_IMAGE_ID;
@@ -517,3 +674,56 @@ __dead2 static void bl1_fwu_done(void *client_cookie, void *reserved)
bl1_plat_fwu_done(client_cookie, reserved);
assert(0);
}
+
+/*******************************************************************************
+ * This function resets an image to IMAGE_STATE_RESET. It fails if the image is
+ * being executed.
+ ******************************************************************************/
+static int bl1_fwu_image_reset(unsigned int image_id, unsigned int flags)
+{
+ image_desc_t *image_desc = bl1_plat_get_image_desc(image_id);
+
+ if ((!image_desc) || (GET_SECURITY_STATE(flags) == SECURE)) {
+ WARN("BL1-FWU: Reset not allowed due to invalid args\n");
+ return -EPERM;
+ }
+
+ switch (image_desc->state) {
+
+ case IMAGE_STATE_RESET:
+ /* Nothing to do. */
+ break;
+
+ case IMAGE_STATE_INTERRUPTED:
+ case IMAGE_STATE_AUTHENTICATED:
+ case IMAGE_STATE_COPIED:
+ case IMAGE_STATE_COPYING:
+
+ if (bl1_fwu_remove_loaded_id(image_id)) {
+ WARN("BL1-FWU: Image reset couldn't find the image ID\n");
+ return -EPERM;
+ }
+
+ /* Clear the memory.*/
+ zero_normalmem((void *)image_desc->image_info.image_base,
+ image_desc->copied_size);
+ flush_dcache_range(image_desc->image_info.image_base,
+ image_desc->copied_size);
+
+ /* Reset status variables */
+ image_desc->copied_size = 0;
+ image_desc->image_info.image_size = 0;
+ image_desc->state = IMAGE_STATE_RESET;
+
+ /* Clear authentication state */
+ auth_img_flags[image_id] = 0;
+
+ break;
+
+ case IMAGE_STATE_EXECUTED:
+ default:
+ assert(0);
+ }
+
+ return 0;
+}