sunxi-tools/fit_image.c
Andre Przywara fa80ab13cf fit-image: Fix endianess conversion
A helper function in fit_image.c was using our self defined portable
endianess conversion function, but was not including our endian header.
This lead to broken builds on some setups:
==========================
fit_image.c: In function 'fdt_getprop_u32':
fit_image.c:86:9: warning: implicit declaration of function 'be32toh'
                  [-Wimplicit-function-declaration]
   86 |  return be32toh(*(uint32_t *)prop->data);
      |         ^~~~~~~
/usr/bin/ld: /tmp/cczFroQN.o: in function `fdt_getprop_u32':
fit_image.c:(.text+0x1e0): undefined reference to `be32toh'
collect2: error: ld returned 1 exit status
==========================

Fix this by using the libfdt provided endianess conversion function,
and using an easier way to get the property on the way.

Signed-off-by: Andre Przywara <osp@andrep.de>
Reported-by: kaidokert
2022-01-13 23:56:34 +00:00

283 lines
7.4 KiB
C

/*
* Copyright (C) 2018-2020 Andre Przywara <osp@andrep.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; under version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdint.h>
#include <libfdt.h>
#include "common.h"
#include "fel_lib.h"
#include "fit_image.h"
/* defined in fel.c */
extern bool verbose;
#define IH_ARCH_INVALID 0
#define IH_ARCH_ARM 2
#define IH_ARCH_ARM64 22
#define IH_OS_INVALID 0
#define IH_OS_LINUX 5
#define IH_OS_U_BOOT 17
#define IH_OS_ARM_TRUSTED_FIRMWARE 25
#define IH_OS_EFI 28
struct fit_image_info {
const char *description;
const char *data;
uint32_t data_size;
uint32_t load_addr;
uint32_t entry_point;
uint8_t os;
uint8_t arch;
};
static int fit_parse_os(const char *value)
{
if (!value || !*value)
return IH_OS_INVALID;
if (!strcmp(value, "u-boot"))
return IH_OS_U_BOOT;
if (!strcmp(value, "linux"))
return IH_OS_LINUX;
if (!strcmp(value, "arm-trusted-firmware"))
return IH_OS_ARM_TRUSTED_FIRMWARE;
if (!strcmp(value, "efi"))
return IH_OS_EFI;
return IH_OS_INVALID;
}
static int fit_parse_arch(const char *value)
{
if (!value || !*value)
return IH_ARCH_INVALID;
if (!strcmp(value, "arm"))
return IH_ARCH_ARM;
if (!strcmp(value, "arm64"))
return IH_ARCH_ARM64;
return IH_ARCH_INVALID;
}
static uint32_t fdt_getprop_u32(const void *fdt, int node, const char *name)
{
const fdt32_t *val;
val = fdt_getprop(fdt, node, name, NULL);
if (!val)
return ~0U;
return fdt32_to_cpu(*val);
}
static const char *fdt_getprop_str(const void *fdt, int node, const char *name)
{
const struct fdt_property *prop;
prop = fdt_get_property(fdt, node, name, NULL);
if (!prop)
return NULL;
return prop->data;
}
/*
* Find the image with the given name under the /images node, and parse
* its information into the fit_image_info struct.
* Returns 0 on success, and a negative error value otherwise.
*/
static int fit_get_image_info(const void *fit, const char *name,
struct fit_image_info *info)
{
int node;
const char *str;
uint32_t data_offset;
node = fdt_path_offset(fit, "/images");
if (node < 0)
return -1;
node = fdt_subnode_offset(fit, node, name);
if (node < 0)
return -1;
info->load_addr = fdt_getprop_u32(fit, node, "load");
info->entry_point = fdt_getprop_u32(fit, node, "entry");
info->description = fdt_getprop_str(fit, node, "description");
/* properties used for FIT images with external data */
info->data_size = fdt_getprop_u32(fit, node, "data-size");
data_offset = fdt_getprop_u32(fit, node, "data-offset");
/* check for embedded data (when invalid external data properties) */
if (info->data_size == ~0U || data_offset == ~0U) {
const struct fdt_property *prop;
int len;
prop = fdt_get_property(fit, node, "data", &len);
info->data_size = len;
info->data = prop->data;
} else {
/* external data is appended at the end of the FIT DTB blob */
info->data = (const char *)fit + ((fdt_totalsize(fit) + 3) & ~3U);
info->data += data_offset;
}
info->os = fit_parse_os(fdt_getprop_str(fit, node, "os"));
info->arch = fit_parse_arch(fdt_getprop_str(fit, node, "arch"));
str = fdt_getprop_str(fit, node, "compression");
/* The current SPL does not support compression either. */
if (str && strcmp(str, "none")) {
printf("compression \"%s\" not supported for image \"%s\"\n",
str, info->description);
return -2;
}
return 0;
}
static int entry_arch;
static uint32_t dtb_addr;
/*
* Upload the image described by its fit_image_info struct to the board.
* Detect if an image contains an entry point and return that.
* Set entry_arch to arm or arm64 on the way. Also detect the image
* containing U-Boot and record its end address, so that the DTB can be
* appended later on.
* Returns the entry point if any is specified, or 0 otherwise.
*/
static uint32_t fit_load_image(feldev_handle *dev, struct fit_image_info *img)
{
uint32_t ret = 0;
if (verbose)
printf("loading image \"%s\" (%d bytes) to 0x%x\n",
img->description, img->data_size, img->load_addr);
aw_fel_write_buffer(dev, img->data,
img->load_addr, img->data_size, true);
if (img->entry_point != ~0U) {
ret = img->entry_point;
entry_arch = img->arch;
}
/* either explicitly marked as U-Boot, or the first invalid one */
if (img->os == IH_OS_U_BOOT ||
(!dtb_addr && img->os == IH_OS_INVALID))
dtb_addr = img->load_addr + img->data_size;
return ret;
}
uint32_t load_fit_images(feldev_handle *dev, const void *fit,
const char *dt_name, bool *use_aarch64)
{
const struct fdt_property *prop;
struct fit_image_info img;
const char *str;
int node, len;
uint32_t entry_point = 0;
node = fdt_path_offset(fit, "/configurations");
if (node < 0) {
pr_error("invalid FIT image, no /configurations node\n");
return 0;
}
/*
* Find the right configuration node, either by using the provided
* DT name as an identifier, falling back to the node titled "default",
* or by using just the first node.
*/
if (dt_name) {
for (node = fdt_first_subnode(fit, node);
node >= 0;
node = fdt_next_subnode(fit, node)) {
prop = fdt_get_property(fit, node, "description", NULL);
if (prop && !strcmp(prop->data, dt_name))
break;
}
if (node < 0) {
pr_error("no matching FIT configuration node for \"%s\"\n",
dt_name);
return 0;
}
} else {
prop = fdt_get_property(fit, node, "default", NULL);
if (!prop)
node = fdt_first_subnode(fit, node);
else
node = fdt_subnode_offset(fit, node, prop->data);
if (node < 0) {
pr_error("no default FIT configuration node\n");
return 0;
}
}
entry_arch = IH_ARCH_INVALID;
dtb_addr = 0;
/* Load the image described as "firmware". */
str = fdt_getprop_str(fit, node, "firmware");
if (str && !fit_get_image_info(fit, str, &img)) {
uint32_t addr = fit_load_image(dev, &img);
if (addr != 0)
entry_point = addr;
} else {
printf("WARNING: no valid \"firmware\" image entry in FIT\n");
}
/* load all loadables, at their respective load addresses */
prop = fdt_get_property(fit, node, "loadables", &len);
for (str = prop ? prop->data : NULL;
prop && (str - prop->data) < len && *str;
str += strlen(str) + 1) {
uint32_t addr;
if (fit_get_image_info(fit, str, &img)) {
printf("Can't load loadable \"%s\", skipping.\n", str);
continue;
}
addr = fit_load_image(dev, &img);
if (addr != 0)
entry_point = addr;
}
if (use_aarch64)
*use_aarch64 = (entry_arch == IH_ARCH_ARM64);
if (!dtb_addr) {
printf("Warning: no U-Boot image found, not loading DTB\n");
return entry_point;
}
/* load .dtb right after the U-Boot image (appended DTB) */
str = fdt_getprop_str(fit, node, "fdt");
if (!str || fit_get_image_info(fit, str, &img)) {
printf("Warning: no FDT found in FIT image\n");
return entry_point;
}
if (verbose)
printf("loading DTB \"%s\" (%d bytes)\n", img.description,
img.data_size);
aw_fel_write_buffer(dev, img.data, dtb_addr, img.data_size, false);
return entry_point;
}