Unverified Commit 6c022244 authored by Chen-Yu Tsai's avatar Chen-Yu Tsai Committed by GitHub
Browse files

Merge pull request #152 from apritzel/fit

sunxi-fel FIT image support
parents 7a6a2221 14b3492e
...@@ -137,9 +137,9 @@ SOC_INFO := soc_info.c soc_info.h ...@@ -137,9 +137,9 @@ SOC_INFO := soc_info.c soc_info.h
FEL_LIB := fel_lib.c fel_lib.h FEL_LIB := fel_lib.c fel_lib.h
SPI_FLASH:= fel-spiflash.c fel-spiflash.h fel-remotefunc-spi-data-transfer.h SPI_FLASH:= fel-spiflash.c fel-spiflash.h fel-remotefunc-spi-data-transfer.h
sunxi-fel: fel.c thunks/fel-to-spl-thunk.h $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH) sunxi-fel: fel.c fit_image.c thunks/fel-to-spl-thunk.h $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH)
$(CC) $(HOST_CFLAGS) $(LIBUSB_CFLAGS) $(ZLIB_CFLAGS) $(LDFLAGS) -o $@ \ $(CC) $(HOST_CFLAGS) $(LIBUSB_CFLAGS) $(ZLIB_CFLAGS) $(LDFLAGS) -o $@ \
$(filter %.c,$^) $(LIBS) $(LIBUSB_LIBS) $(ZLIB_LIBS) $(filter %.c,$^) $(LIBS) $(LIBUSB_LIBS) $(ZLIB_LIBS) -lfdt
sunxi-nand-part: nand-part-main.c nand-part.c nand-part-a10.h nand-part-a20.h sunxi-nand-part: nand-part-main.c nand-part.c nand-part-a10.h nand-part-a20.h
$(CC) $(HOST_CFLAGS) -c -o nand-part-main.o nand-part-main.c $(CC) $(HOST_CFLAGS) -c -o nand-part-main.o nand-part-main.c
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "portable_endian.h" #include "portable_endian.h"
#include "fel_lib.h" #include "fel_lib.h"
#include "fel-spiflash.h" #include "fel-spiflash.h"
#include "fit_image.h"
#include <assert.h> #include <assert.h>
#include <ctype.h> #include <ctype.h>
...@@ -31,9 +32,10 @@ ...@@ -31,9 +32,10 @@
#include <zlib.h> #include <zlib.h>
#include <sys/stat.h> #include <sys/stat.h>
static bool verbose = false; /* If set, makes the 'fel' tool more talkative */ bool verbose = false; /* If set, makes the 'fel' tool more talkative */
static uint32_t uboot_entry = 0; /* entry point (address) of U-Boot */ static uint32_t uboot_entry = 0; /* entry point (address) of U-Boot */
static uint32_t uboot_size = 0; /* size of U-Boot binary */ static uint32_t uboot_size = 0; /* size of U-Boot binary */
static bool enter_in_aarch64 = false;
/* printf-style output, but only if "verbose" flag is active */ /* printf-style output, but only if "verbose" flag is active */
#define pr_info(...) \ #define pr_info(...) \
...@@ -45,6 +47,7 @@ static uint32_t uboot_size = 0; /* size of U-Boot binary */ ...@@ -45,6 +47,7 @@ static uint32_t uboot_size = 0; /* size of U-Boot binary */
#define IH_TYPE_INVALID 0 /* Invalid Image */ #define IH_TYPE_INVALID 0 /* Invalid Image */
#define IH_TYPE_FIRMWARE 5 /* Firmware Image */ #define IH_TYPE_FIRMWARE 5 /* Firmware Image */
#define IH_TYPE_SCRIPT 6 /* Script file */ #define IH_TYPE_SCRIPT 6 /* Script file */
#define IH_TYPE_FLATDT 8 /* DTB or FIT image */
#define IH_NMLEN 32 /* Image Name Length */ #define IH_NMLEN 32 /* Image Name Length */
/* Additional error codes, newly introduced for get_image_type() */ /* Additional error codes, newly introduced for get_image_type() */
...@@ -90,6 +93,8 @@ int get_image_type(const uint8_t *buf, size_t len) ...@@ -90,6 +93,8 @@ int get_image_type(const uint8_t *buf, size_t len)
if (len <= HEADER_SIZE) /* insufficient length/size */ if (len <= HEADER_SIZE) /* insufficient length/size */
return IH_TYPE_INVALID; return IH_TYPE_INVALID;
if (be32toh(hdr->ih_magic) == 0xd00dfeed)
return IH_TYPE_FLATDT;
if (be32toh(hdr->ih_magic) != IH_MAGIC) /* signature mismatch */ if (be32toh(hdr->ih_magic) != IH_MAGIC) /* signature mismatch */
return IH_TYPE_INVALID; return IH_TYPE_INVALID;
/* For sunxi, we always expect ARM architecture here */ /* For sunxi, we always expect ARM architecture here */
...@@ -864,7 +869,8 @@ uint32_t aw_fel_write_and_execute_spl(feldev_handle *dev, uint8_t *buf, size_t l ...@@ -864,7 +869,8 @@ uint32_t aw_fel_write_and_execute_spl(feldev_handle *dev, uint8_t *buf, size_t l
* address stored within the image header; and the function preserves the * address stored within the image header; and the function preserves the
* U-Boot entry point (offset) and size values. * U-Boot entry point (offset) and size values.
*/ */
void aw_fel_write_uboot_image(feldev_handle *dev, uint8_t *buf, size_t len) static void aw_fel_write_uboot_image(feldev_handle *dev, uint8_t *buf,
size_t len, const char *dt_name)
{ {
if (len <= HEADER_SIZE) if (len <= HEADER_SIZE)
return; /* Insufficient size (no actual data), just bail out */ return; /* Insufficient size (no actual data), just bail out */
...@@ -887,6 +893,13 @@ void aw_fel_write_uboot_image(feldev_handle *dev, uint8_t *buf, size_t len) ...@@ -887,6 +893,13 @@ void aw_fel_write_uboot_image(feldev_handle *dev, uint8_t *buf, size_t len)
} }
exit(1); exit(1);
} }
if (image_type == IH_TYPE_FLATDT) { /* FIT image */
uboot_entry = load_fit_images(dev, buf, dt_name,
&enter_in_aarch64);
uboot_size = 4; /* dummy value to pass check below */
return;
}
if (image_type != IH_TYPE_FIRMWARE) if (image_type != IH_TYPE_FIRMWARE)
pr_fatal("U-Boot image type mismatch: " pr_fatal("U-Boot image type mismatch: "
"expected IH_TYPE_FIRMWARE, got %02X\n", image_type); "expected IH_TYPE_FIRMWARE, got %02X\n", image_type);
...@@ -923,6 +936,28 @@ void aw_fel_write_uboot_image(feldev_handle *dev, uint8_t *buf, size_t len) ...@@ -923,6 +936,28 @@ void aw_fel_write_uboot_image(feldev_handle *dev, uint8_t *buf, size_t len)
uboot_size = data_size; uboot_size = data_size;
} }
static const char *spl_get_dtb_name(uint8_t *spl_buf)
{
uint32_t dt_offset;
if (memcmp(spl_buf + 4, "eGON.BT0", 8))
return NULL;
if (memcmp(spl_buf + 0x14, "SPL", 3))
return NULL;
if (spl_buf[0x17] < 0x2) /* only since v0.2 */
return NULL;
memcpy(&dt_offset, spl_buf + 0x20, 4);
dt_offset = le32toh(dt_offset);
if (verbose)
printf("found DT name in SPL header: %s\n", spl_buf + dt_offset);
return (char *)spl_buf + dt_offset;
}
/* /*
* This function handles the common part of both "spl" and "uboot" commands. * This function handles the common part of both "spl" and "uboot" commands.
*/ */
...@@ -932,15 +967,18 @@ void aw_fel_process_spl_and_uboot(feldev_handle *dev, const char *filename) ...@@ -932,15 +967,18 @@ void aw_fel_process_spl_and_uboot(feldev_handle *dev, const char *filename)
uint32_t offset; uint32_t offset;
/* load file into memory buffer */ /* load file into memory buffer */
uint8_t *buf = load_file(filename, &size); uint8_t *buf = load_file(filename, &size);
const char *dt_name = spl_get_dtb_name(buf);
/* write and execute the SPL from the buffer */ /* write and execute the SPL from the buffer */
offset = aw_fel_write_and_execute_spl(dev, buf, size); offset = aw_fel_write_and_execute_spl(dev, buf, size);
/* check for optional main U-Boot binary (and transfer it, if applicable) */ /* check for optional main U-Boot binary (and transfer it, if applicable) */
if (size > offset) { if (size > offset) {
/* U-Boot pads to at least 32KB */ /* U-Boot pads to at least 32KB */
if (offset < SPL_MIN_OFFSET) if (offset < SPL_MIN_OFFSET)
offset = SPL_MIN_OFFSET; offset = SPL_MIN_OFFSET;
aw_fel_write_uboot_image(dev, buf + offset, size - offset); aw_fel_write_uboot_image(dev, buf + offset, size - offset,
dt_name);
} }
free(buf); free(buf);
} }
...@@ -1405,7 +1443,10 @@ int main(int argc, char **argv) ...@@ -1405,7 +1443,10 @@ int main(int argc, char **argv)
/* auto-start U-Boot if requested (by the "uboot" command) */ /* auto-start U-Boot if requested (by the "uboot" command) */
if (uboot_autostart) { if (uboot_autostart) {
pr_info("Starting U-Boot (0x%08X).\n", uboot_entry); pr_info("Starting U-Boot (0x%08X).\n", uboot_entry);
aw_fel_execute(handle, uboot_entry); if (enter_in_aarch64)
aw_rmr_request(handle, uboot_entry, true);
else
aw_fel_execute(handle, uboot_entry);
} }
feldev_done(handle); feldev_done(handle);
......
...@@ -214,8 +214,11 @@ void aw_fel_read(feldev_handle *dev, uint32_t offset, void *buf, size_t len) ...@@ -214,8 +214,11 @@ void aw_fel_read(feldev_handle *dev, uint32_t offset, void *buf, size_t len)
} }
/* AW_FEL_1_WRITE request */ /* AW_FEL_1_WRITE request */
void aw_fel_write(feldev_handle *dev, void *buf, uint32_t offset, size_t len) void aw_fel_write(feldev_handle *dev, const void *buf, uint32_t offset, size_t len)
{ {
if (len == 0)
return;
aw_send_fel_request(dev, AW_FEL_1_WRITE, offset, len); aw_send_fel_request(dev, AW_FEL_1_WRITE, offset, len);
aw_usb_write(dev, buf, len, false); aw_usb_write(dev, buf, len, false);
aw_read_fel_status(dev); aw_read_fel_status(dev);
...@@ -233,9 +236,12 @@ void aw_fel_execute(feldev_handle *dev, uint32_t offset) ...@@ -233,9 +236,12 @@ void aw_fel_execute(feldev_handle *dev, uint32_t offset)
* Unlike aw_fel_write() above - which is reserved for internal use - this * Unlike aw_fel_write() above - which is reserved for internal use - this
* routine optionally allows progress callbacks. * routine optionally allows progress callbacks.
*/ */
void aw_fel_write_buffer(feldev_handle *dev, void *buf, uint32_t offset, void aw_fel_write_buffer(feldev_handle *dev, const void *buf, uint32_t offset,
size_t len, bool progress) size_t len, bool progress)
{ {
if (len == 0)
return;
aw_send_fel_request(dev, AW_FEL_1_WRITE, offset, len); aw_send_fel_request(dev, AW_FEL_1_WRITE, offset, len);
aw_usb_write(dev, buf, len, progress); aw_usb_write(dev, buf, len, progress);
aw_read_fel_status(dev); aw_read_fel_status(dev);
......
...@@ -59,8 +59,8 @@ feldev_list_entry *list_fel_devices(size_t *count); ...@@ -59,8 +59,8 @@ feldev_list_entry *list_fel_devices(size_t *count);
/* FEL functions */ /* FEL functions */
void aw_fel_read(feldev_handle *dev, uint32_t offset, void *buf, size_t len); void aw_fel_read(feldev_handle *dev, uint32_t offset, void *buf, size_t len);
void aw_fel_write(feldev_handle *dev, void *buf, uint32_t offset, size_t len); void aw_fel_write(feldev_handle *dev, const void *buf, uint32_t offset, size_t len);
void aw_fel_write_buffer(feldev_handle *dev, void *buf, uint32_t offset, void aw_fel_write_buffer(feldev_handle *dev, const void *buf, uint32_t offset,
size_t len, bool progress); size_t len, bool progress);
void aw_fel_execute(feldev_handle *dev, uint32_t offset); void aw_fel_execute(feldev_handle *dev, uint32_t offset);
......
/*
* 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 struct fdt_property *prop;
prop = fdt_get_property(fdt, node, name, NULL);
if (!prop)
return ~0U;
return be32toh(*(uint32_t *)prop->data);
}
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;
}
/*
* 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/>.
*/
#ifndef __FIT_IMAGE_H__
#define __FIT_IMAGE_H__
#include <stdint.h>
#include "fel_lib.h"
/*
* Load all images referenced in the given U-Boot FIT image. @dt_name will
* be used to select one of the configurations. @use_aarch64 contains the
* target architecture of the entry point.
* Returns the entry point address of the image to be started.
*/
uint32_t load_fit_images(feldev_handle *dev, const void *fit,
const char *dt_name, bool *use_aarch64);
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment