| /* SPDX-License-Identifier: GPL-2.0+ */ |
| /* |
| * Copyright (C) Sean Anderson <seanga2@gmail.com> |
| */ |
| #ifndef _SPL_LOAD_H_ |
| #define _SPL_LOAD_H_ |
| |
| #include <image.h> |
| #include <imx_container.h> |
| #include <mapmem.h> |
| #include <spl.h> |
| |
| static inline int _spl_load(struct spl_image_info *spl_image, |
| const struct spl_boot_device *bootdev, |
| struct spl_load_info *info, size_t size, |
| size_t offset) |
| { |
| struct legacy_img_hdr *header = |
| spl_get_load_buffer(-sizeof(*header), sizeof(*header)); |
| ulong base_offset, image_offset, overhead; |
| int read, ret; |
| |
| read = info->read(info, offset, ALIGN(sizeof(*header), |
| spl_get_bl_len(info)), header); |
| if (read < sizeof(*header)) |
| return -EIO; |
| |
| if (image_get_magic(header) == FDT_MAGIC) { |
| if (IS_ENABLED(CONFIG_SPL_LOAD_FIT_FULL)) { |
| void *buf; |
| |
| /* |
| * In order to support verifying images in the FIT, we |
| * need to load the whole FIT into memory. Try and |
| * guess how much we need to load by using the total |
| * size. This will fail for FITs with external data, |
| * but there's not much we can do about that. |
| */ |
| if (!size) |
| size = round_up(fdt_totalsize(header), 4); |
| buf = map_sysmem(CONFIG_SYS_LOAD_ADDR, size); |
| read = info->read(info, offset, |
| ALIGN(size, spl_get_bl_len(info)), |
| buf); |
| if (read < size) |
| return -EIO; |
| |
| return spl_parse_image_header(spl_image, bootdev, buf); |
| } |
| |
| if (IS_ENABLED(CONFIG_SPL_LOAD_FIT)) |
| return spl_load_simple_fit(spl_image, info, offset, |
| header); |
| } |
| |
| if (IS_ENABLED(CONFIG_SPL_LOAD_IMX_CONTAINER) && |
| valid_container_hdr((void *)header)) |
| return spl_load_imx_container(spl_image, info, offset); |
| |
| if (IS_ENABLED(CONFIG_SPL_LZMA) && |
| image_get_magic(header) == IH_MAGIC && |
| image_get_comp(header) == IH_COMP_LZMA) { |
| spl_image->flags |= SPL_COPY_PAYLOAD_ONLY; |
| ret = spl_parse_image_header(spl_image, bootdev, header); |
| if (ret) |
| return ret; |
| |
| return spl_load_legacy_lzma(spl_image, info, offset); |
| } |
| |
| ret = spl_parse_image_header(spl_image, bootdev, header); |
| if (ret) |
| return ret; |
| |
| base_offset = spl_image->offset; |
| /* Only NOR sets this flag. */ |
| if (IS_ENABLED(CONFIG_SPL_NOR_SUPPORT) && |
| spl_image->flags & SPL_COPY_PAYLOAD_ONLY) |
| base_offset += sizeof(*header); |
| image_offset = ALIGN_DOWN(base_offset, spl_get_bl_len(info)); |
| overhead = base_offset - image_offset; |
| size = ALIGN(spl_image->size + overhead, spl_get_bl_len(info)); |
| |
| read = info->read(info, offset + image_offset, size, |
| map_sysmem(spl_image->load_addr - overhead, size)); |
| return read < spl_image->size ? -EIO : 0; |
| } |
| |
| /* |
| * Although spl_load results in size reduction for callers, this is generally |
| * not enough to counteract the bloat if there is only one caller. The core |
| * problem is that the compiler can't optimize across translation units. The |
| * general solution to this is CONFIG_LTO, but that is not available on all |
| * architectures. Perform a pseudo-LTO just for this function by declaring it |
| * inline if there is one caller, and extern otherwise. |
| */ |
| #define SPL_LOAD_USERS \ |
| IS_ENABLED(CONFIG_SPL_FS_EXT4) + \ |
| IS_ENABLED(CONFIG_SPL_FS_FAT) + \ |
| IS_ENABLED(CONFIG_SPL_SYS_MMCSD_RAW_MODE) + \ |
| (IS_ENABLED(CONFIG_SPL_NAND_SUPPORT) && !IS_ENABLED(CONFIG_SPL_UBI)) + \ |
| IS_ENABLED(CONFIG_SPL_NET) + \ |
| 0 |
| |
| #if SPL_LOAD_USERS > 1 |
| /** |
| * spl_load() - Parse a header and load the image |
| * @spl_image: Image data which will be filled in by this function |
| * @bootdev: The device to load from |
| * @info: Describes how to load additional information from @bootdev. At the |
| * minimum, read() and bl_len must be populated. |
| * @size: The size of the image, in bytes, if it is known in advance. Some boot |
| * devices (such as filesystems) know how big an image is before parsing |
| * the header. If 0, then the size will be determined from the header. |
| * @offset: The offset from the start of @bootdev, in bytes. This should have |
| * the offset @header was loaded from. It will be added to any offsets |
| * passed to @info->read(). |
| * |
| * This function determines the image type (FIT, legacy, i.MX, raw, etc), calls |
| * the appropriate parsing function, determines the load address, and the loads |
| * the image from storage. It is designed to replace ad-hoc image loading which |
| * may not support all image types (especially when config options are |
| * involved). |
| * |
| * Return: 0 on success, or a negative error on failure |
| */ |
| int spl_load(struct spl_image_info *spl_image, |
| const struct spl_boot_device *bootdev, struct spl_load_info *info, |
| size_t size, size_t offset); |
| #else |
| static inline int spl_load(struct spl_image_info *spl_image, |
| const struct spl_boot_device *bootdev, |
| struct spl_load_info *info, size_t size, |
| size_t offset) |
| { |
| return _spl_load(spl_image, bootdev, info, size, offset); |
| } |
| #endif |
| |
| #endif /* _SPL_LOAD_H_ */ |