Add SPL_FIT_CIPHER and decrypt FIT image data before post-processing,
decompression or moving it to the final load address.

SPL cannot always allocate a new output buffer while loading FIT images, so
use the caller-provided decrypt-to-buffer helper. External encrypted images
are read into scratch memory first, then decrypted either in-place for
compressed payloads or into a non-overlapping buffer for direct loads.
Embedded encrypted images decrypt into the final load buffer or a scratch
buffer when decompression is still required.

Signed-off-by: James Hilliard <[email protected]>
---
 boot/Kconfig         |  9 +++++
 boot/image-fit.c     |  2 +-
 common/spl/spl_fit.c | 92 +++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 96 insertions(+), 7 deletions(-)

diff --git a/boot/Kconfig b/boot/Kconfig
index 8e468c56176..19bbeb9ad36 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -155,6 +155,15 @@ config FIT_CIPHER
          Enable the feature of data ciphering/unciphering in the tool mkimage
          and in the u-boot support of the FIT image.
 
+config SPL_FIT_CIPHER
+       bool "Enable ciphering data in SPL FIT images"
+       depends on SPL_LOAD_FIT
+       depends on SPL_DM_AES
+       select SPL_FIT
+       help
+         Enable unciphering of FIT image data in SPL. This allows SPL to
+         decrypt an encrypted U-Boot proper FIT image through an AES driver.
+
 config FIT_VERITY
        bool "dm-verity boot parameter generation from FIT metadata"
        depends on FIT && OF_LIBFDT
diff --git a/boot/image-fit.c b/boot/image-fit.c
index dac8b35f338..1d1723bdae0 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -2290,7 +2290,7 @@ int fit_image_load(struct bootm_headers *images, ulong 
addr,
        }
 
        /* Decrypt data before uncompress/move */
-       if (IS_ENABLED(CONFIG_FIT_CIPHER) && IMAGE_ENABLE_DECRYPT) {
+       if (!tools_build() && IMAGE_ENABLE_DECRYPT) {
                puts("   Decrypting Data ... ");
                if (fit_image_uncipher(fit, noffset, &buf, &size)) {
                        puts("Error\n");
diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c
index d89384449b3..57975a7638f 100644
--- a/common/spl/spl_fit.c
+++ b/common/spl/spl_fit.c
@@ -16,6 +16,7 @@
 #include <sysinfo.h>
 #include <asm/global_data.h>
 #include <asm/io.h>
+#include <limits.h>
 #include <linux/libfdt.h>
 #include <linux/printk.h>
 
@@ -193,6 +194,47 @@ static int get_aligned_image_size(struct spl_load_info 
*info, int data_size,
        return ALIGN(data_size, spl_get_bl_len(info));
 }
 
+static int spl_fit_image_decrypt(const void *fit, int node, int cipher_node,
+                                void **data, size_t *size, void *dst)
+{
+       size_t dst_size;
+       int ret;
+
+       puts("   Decrypting Data ... ");
+       ret = fit_image_decrypt_data_to(fit, node, cipher_node, *data, *size,
+                                       dst, &dst_size);
+       if (ret) {
+               puts("Error\n");
+               return ret;
+       }
+
+       *data = dst;
+       *size = dst_size;
+
+       puts("OK\n");
+
+       return 0;
+}
+
+static bool spl_fit_ranges_overlap(ulong addr1, size_t size1,
+                                  ulong addr2, size_t size2)
+{
+       ulong end1, end2;
+
+       if (!size1 || !size2)
+               return false;
+
+       end1 = addr1 + size1;
+       if (end1 < addr1)
+               end1 = ULONG_MAX;
+
+       end2 = addr2 + size2;
+       if (end2 < addr2)
+               end2 = ULONG_MAX;
+
+       return addr1 < end2 && addr2 < end1;
+}
+
 /**
  * load_simple_fit(): load the image described in a certain FIT node
  * @info:      points to information about the device to load data from
@@ -225,12 +267,15 @@ static int load_simple_fit(struct spl_load_info *info, 
ulong fit_offset,
        const void *data;
        const void *fit = ctx->fit;
        bool external_data = false;
+       bool encrypted;
+       bool needs_decomp = false;
+       int cipher_node = -ENOENT;
+       int ret;
 
        log_debug("starting\n");
        if (CONFIG_IS_ENABLED(BOOTMETH_VBE) &&
            xpl_get_phase(info) != IH_PHASE_NONE) {
                enum image_phase_t phase;
-               int ret;
 
                ret = fit_image_get_phase(fit, node, &phase);
                /* if the image is for any phase, let's use it */
@@ -256,6 +301,8 @@ static int load_simple_fit(struct spl_load_info *info, 
ulong fit_offset,
        if (spl_decompression_enabled()) {
                fit_image_get_comp(fit, node, &image_comp);
                debug("%s ", genimg_get_comp_name(image_comp));
+               needs_decomp = image_comp == IH_COMP_GZIP ||
+                              image_comp == IH_COMP_LZMA;
        }
 
        if (fit_image_get_load(fit, node, &load_addr)) {
@@ -267,6 +314,10 @@ static int load_simple_fit(struct spl_load_info *info, 
ulong fit_offset,
                load_addr = image_info->load_addr;
        }
 
+       if (CONFIG_IS_ENABLED(FIT_CIPHER))
+               cipher_node = fdt_subnode_offset(fit, node, 
FIT_CIPHER_NODENAME);
+       encrypted = cipher_node >= 0;
+
        if (!fit_image_get_data_position(fit, node, &offset)) {
                external_data = true;
        } else if (!fit_image_get_data_offset(fit, node, &offset)) {
@@ -291,11 +342,12 @@ static int load_simple_fit(struct spl_load_info *info, 
ulong fit_offset,
                        return 0;
                }
 
-               if (spl_decompression_enabled() &&
-                   (image_comp == IH_COMP_GZIP || image_comp == IH_COMP_LZMA))
-                       src_ptr = map_sysmem(ALIGN(CONFIG_SYS_LOAD_ADDR, 
ARCH_DMA_MINALIGN), len);
+               if (needs_decomp || encrypted)
+                       src_ptr = map_sysmem(ALIGN(CONFIG_SYS_LOAD_ADDR,
+                                                  ARCH_DMA_MINALIGN), len);
                else
-                       src_ptr = map_sysmem(ALIGN(load_addr, 
ARCH_DMA_MINALIGN), len);
+                       src_ptr = map_sysmem(ALIGN(load_addr, 
ARCH_DMA_MINALIGN),
+                                            len);
                length = len;
 
                overhead = get_aligned_image_overhead(info, offset);
@@ -331,10 +383,38 @@ static int load_simple_fit(struct spl_load_info *info, 
ulong fit_offset,
                puts("OK\n");
        }
 
+       load_ptr = map_sysmem(load_addr, length);
+       if (encrypted) {
+               void *decrypt_ptr;
+
+               if (needs_decomp) {
+                       if (external_data)
+                               decrypt_ptr = src;
+                       else
+                               decrypt_ptr = 
map_sysmem(ALIGN(CONFIG_SYS_LOAD_ADDR,
+                                                              
ARCH_DMA_MINALIGN),
+                                                        length);
+               } else if (external_data) {
+                       if (spl_fit_ranges_overlap((ulong)load_ptr, length,
+                                                  (ulong)src, length))
+                               decrypt_ptr = map_sysmem(ALIGN((ulong)src + 
length,
+                                                              
ARCH_DMA_MINALIGN),
+                                                        length);
+                       else
+                               decrypt_ptr = load_ptr;
+               } else {
+                       decrypt_ptr = load_ptr;
+               }
+
+               ret = spl_fit_image_decrypt(fit, node, cipher_node, &src, 
&length,
+                                           decrypt_ptr);
+               if (ret)
+                       return ret;
+       }
+
        if (CONFIG_IS_ENABLED(FIT_IMAGE_POST_PROCESS))
                board_fit_image_post_process(fit, node, &src, &length);
 
-       load_ptr = map_sysmem(load_addr, length);
        if (IS_ENABLED(CONFIG_SPL_GZIP) && image_comp == IH_COMP_GZIP) {
                size = length;
                if (gunzip(load_ptr, CONFIG_SYS_BOOTM_LEN, src, &size)) {
-- 
2.53.0

Reply via email to