551 lines
12 KiB
C
551 lines
12 KiB
C
/*
|
|
* (C) Copyright 2019 Rockchip Electronics Co., Ltd
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
#include <common.h>
|
|
#include <boot_rkimg.h>
|
|
#include <image.h>
|
|
#include <malloc.h>
|
|
#include <sysmem.h>
|
|
#include <asm/arch/fit.h>
|
|
#include <asm/arch/resource_img.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
#define FIT_PLACEHOLDER_ADDR 0xffffff00
|
|
|
|
/*
|
|
* Must use args '-E -p' for mkimage to generate FIT image, 4K as max assumption.
|
|
*/
|
|
#define FIT_FDT_MAX_SIZE SZ_4K
|
|
|
|
static int fit_is_ext_type(void *fit)
|
|
{
|
|
return fdt_totalsize(fit) < FIT_FDT_MAX_SIZE;
|
|
}
|
|
|
|
static int fit_is_signed(void *fit, const void *sig_blob)
|
|
{
|
|
return fdt_subnode_offset(sig_blob, 0, FIT_SIG_NODENAME) < 0 ? 0 : 1;
|
|
}
|
|
|
|
static inline int fit_is_placeholder_addr(ulong addr)
|
|
{
|
|
return (addr & 0xffffff00) == FIT_PLACEHOLDER_ADDR;
|
|
}
|
|
|
|
static int fit_is_required(void *fit, const void *sig_blob)
|
|
{
|
|
int sig_node;
|
|
int noffset;
|
|
|
|
sig_node = fdt_subnode_offset(sig_blob, 0, FIT_SIG_NODENAME);
|
|
if (sig_node < 0)
|
|
return 0;
|
|
|
|
fdt_for_each_subnode(noffset, sig_blob, sig_node) {
|
|
const char *required;
|
|
|
|
required = fdt_getprop(sig_blob, noffset, "required", NULL);
|
|
if (required && !strcmp(required, "conf"))
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fit_fixup_load_entry(void *fit, int images, int defconf,
|
|
char *name, ulong *load, ulong new_addr)
|
|
{
|
|
const char *uname;
|
|
int uname_cfg;
|
|
int err;
|
|
|
|
if (!fit_is_placeholder_addr(*load) ||
|
|
fit_is_required(fit, gd_fdt_blob()))
|
|
return 0;
|
|
|
|
*load = new_addr;
|
|
|
|
uname = fdt_getprop(fit, defconf, name, NULL);
|
|
if (!uname)
|
|
return -ENODEV;
|
|
|
|
uname_cfg = fdt_subnode_offset(fit, images, uname);
|
|
if (uname_cfg < 0)
|
|
return -ENODEV;
|
|
|
|
err = fit_image_set_load(fit, uname_cfg, new_addr);
|
|
if (err)
|
|
return err;
|
|
|
|
fit_image_set_entry(fit, uname_cfg, new_addr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fit_get_load_and_data(void *fit, int images, int defconf,
|
|
const char *name, ulong *load,
|
|
int *offset, int *size)
|
|
{
|
|
const char *uname;
|
|
int uname_cfg;
|
|
int off, sz;
|
|
int err;
|
|
|
|
uname = fdt_getprop(fit, defconf, name, NULL);
|
|
if (!uname)
|
|
return -ENODEV;
|
|
|
|
uname_cfg = fdt_subnode_offset(fit, images, uname);
|
|
if (uname_cfg < 0)
|
|
return -ENODEV;
|
|
|
|
err = fit_image_get_data_size(fit, uname_cfg, &sz);
|
|
if (err)
|
|
return err;
|
|
|
|
err = fit_image_get_data_position(fit, uname_cfg, &off);
|
|
if (!err) {
|
|
off -= fdt_totalsize(fit);
|
|
} else {
|
|
err = fit_image_get_data_offset(fit, uname_cfg, &off);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
/* optional */
|
|
if (load) {
|
|
err = fit_image_get_load(fit, uname_cfg, load);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
*offset = off;
|
|
*size = sz;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fit_image_fixup_and_sysmem_rsv(void *fit)
|
|
{
|
|
ulong load, kaddr, faddr, raddr;
|
|
int images, defconf;
|
|
int offset, size;
|
|
int err;
|
|
|
|
faddr = env_get_ulong("fdt_addr_r", 16, 0);
|
|
kaddr = env_get_ulong("kernel_addr_r", 16, 0);
|
|
raddr = env_get_ulong("ramdisk_addr_r", 16, 0);
|
|
|
|
if (!faddr || !kaddr || !raddr)
|
|
return -EINVAL;
|
|
|
|
if (fit_get_image_defconf_node(fit, &images, &defconf)) {
|
|
FIT_I("Failed to get default config\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* fdt */
|
|
if (fit_get_load_and_data(fit, images, defconf, FIT_FDT_PROP,
|
|
&load, &offset, &size)) {
|
|
FIT_I("Invalid fdt node\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
#ifdef CONFIG_USING_KERNEL_DTB
|
|
sysmem_free((phys_addr_t)gd->fdt_blob);
|
|
#endif
|
|
if (fit_fixup_load_entry(fit, images, defconf,
|
|
FIT_FDT_PROP, &load, faddr)) {
|
|
FIT_I("Failed to fixup fdt load addr\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!sysmem_alloc_base(MEM_FDT, (phys_addr_t)load,
|
|
ALIGN(size, RK_BLK_SIZE)))
|
|
return -ENOMEM;
|
|
|
|
/* kernel */
|
|
if (fit_get_load_and_data(fit, images, defconf, FIT_KERNEL_PROP,
|
|
&load, &offset, &size)) {
|
|
FIT_I("Invalid kernel node\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (fit_fixup_load_entry(fit, images, defconf,
|
|
FIT_KERNEL_PROP, &load, kaddr)) {
|
|
FIT_I("Failed to fixup kernel load addr\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!sysmem_alloc_base(MEM_KERNEL, (phys_addr_t)load,
|
|
ALIGN(size, RK_BLK_SIZE)))
|
|
return -ENOMEM;
|
|
|
|
/* ramdisk(optional) */
|
|
err = fit_get_load_and_data(fit, images, defconf, FIT_RAMDISK_PROP,
|
|
&load, &offset, &size);
|
|
if (err && err != -ENODEV) {
|
|
FIT_I("Invalid ramdisk node\n");
|
|
return err;
|
|
}
|
|
|
|
if (size) {
|
|
if (fit_fixup_load_entry(fit, images, defconf,
|
|
FIT_RAMDISK_PROP, &load, raddr)) {
|
|
FIT_I("Failed to fixup ramdisk load addr\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!sysmem_alloc_base(MEM_RAMDISK, (phys_addr_t)load,
|
|
ALIGN(size, RK_BLK_SIZE)))
|
|
return -ENOMEM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fit_sysmem_free_each(void *fit)
|
|
{
|
|
ulong raddr, kaddr, faddr;
|
|
|
|
raddr = env_get_ulong("ramdisk_addr_r", 16, 0);
|
|
kaddr = env_get_ulong("kernel_addr_r", 16, 0);
|
|
faddr = env_get_ulong("fdt_addr_r", 16, 0);
|
|
|
|
sysmem_free((phys_addr_t)fit);
|
|
sysmem_free((phys_addr_t)raddr);
|
|
sysmem_free((phys_addr_t)kaddr);
|
|
sysmem_free((phys_addr_t)faddr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fit_image_load_one(void *fit, struct blk_desc *dev_desc,
|
|
disk_partition_t *part, int images,
|
|
int defconf, char *name, void *dst)
|
|
{
|
|
u32 blknum, blkoff;
|
|
int offset, size;
|
|
|
|
if (fit_get_load_and_data(fit, images, defconf, name,
|
|
NULL, &offset, &size))
|
|
return -EINVAL;
|
|
|
|
blkoff = (FIT_ALIGN(fdt_totalsize(fit)) + offset) / dev_desc->blksz;
|
|
blknum = DIV_ROUND_UP(size, dev_desc->blksz);
|
|
|
|
if (blk_dread(dev_desc, part->start + blkoff, blknum, dst) != blknum)
|
|
return -EIO;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fit_image_load_fdt(void *fit, struct blk_desc *dev_desc,
|
|
disk_partition_t *part, int images,
|
|
int defconf, void *dst)
|
|
{
|
|
return fit_image_load_one(fit, dev_desc, part, images,
|
|
defconf, FIT_FDT_PROP, dst);
|
|
}
|
|
|
|
#ifdef CONFIG_ROCKCHIP_RESOURCE_IMAGE
|
|
static int fit_image_load_resource(void *fit, struct blk_desc *dev_desc,
|
|
disk_partition_t *part, int images,
|
|
int defconf, ulong *addr)
|
|
{
|
|
ulong fdt_addr_r, dst;
|
|
int offset, size;
|
|
int err;
|
|
|
|
err = fit_get_load_and_data(fit, images, defconf, FIT_MULTI_PROP,
|
|
NULL, &offset, &size);
|
|
if (err)
|
|
return err;
|
|
|
|
fdt_addr_r = env_get_ulong("fdt_addr_r", 16, 0);
|
|
if (!fdt_addr_r)
|
|
return -EINVAL;
|
|
|
|
/* reserve enough space before fdt */
|
|
dst = fdt_addr_r -
|
|
ALIGN(size, dev_desc->blksz) - CONFIG_SYS_FDT_PAD;
|
|
|
|
if (!sysmem_alloc_base(MEM_RESOURCE, (phys_addr_t)dst,
|
|
ALIGN(size, dev_desc->blksz)))
|
|
return -ENOMEM;
|
|
|
|
*addr = dst;
|
|
|
|
return fit_image_load_one(fit, dev_desc, part, images, defconf,
|
|
FIT_MULTI_PROP, (void *)dst);
|
|
}
|
|
#endif
|
|
|
|
static void *fit_get_blob(struct blk_desc *dev_desc, disk_partition_t *part)
|
|
{
|
|
void *fit, *fdt;
|
|
int blknum;
|
|
|
|
blknum = DIV_ROUND_UP(sizeof(struct fdt_header), dev_desc->blksz);
|
|
fdt = memalign(ARCH_DMA_MINALIGN, blknum * dev_desc->blksz);
|
|
if (!fdt)
|
|
return NULL;
|
|
|
|
if (blk_dread(dev_desc, part->start, blknum, fdt) != blknum) {
|
|
debug("Failed to read fdt header\n");
|
|
goto fail;
|
|
}
|
|
|
|
if (fdt_check_header(fdt)) {
|
|
debug("Invalid fdt header\n");
|
|
goto fail;
|
|
}
|
|
|
|
if (!fit_is_ext_type(fdt)) {
|
|
debug("Not external type\n");
|
|
goto fail;
|
|
}
|
|
|
|
blknum = DIV_ROUND_UP(fdt_totalsize(fdt), dev_desc->blksz);
|
|
fit = memalign(ARCH_DMA_MINALIGN, blknum * dev_desc->blksz);
|
|
if (!fit) {
|
|
debug("No memory\n");
|
|
goto fail;
|
|
}
|
|
|
|
if (blk_dread(dev_desc, part->start, blknum, fit) != blknum) {
|
|
free(fit);
|
|
debug("Failed to read fit\n");
|
|
goto fail;
|
|
}
|
|
|
|
return fit;
|
|
|
|
fail:
|
|
free(fdt);
|
|
return NULL;
|
|
}
|
|
|
|
static int fit_image_get_fdt_hash(void *fit, int images, int defconf,
|
|
char **hash, int *hash_size)
|
|
{
|
|
const char *fdt_name;
|
|
const char *name;
|
|
uint8_t *fit_value2;
|
|
uint8_t *fit_value;
|
|
int fit_value_len;
|
|
int hash_off;
|
|
int fdt_off;
|
|
int found = 0;
|
|
char *algo;
|
|
|
|
fdt_name = fdt_getprop(fit, defconf, FIT_FDT_PROP, NULL);
|
|
if (!fdt_name)
|
|
return -EBADF;
|
|
|
|
fdt_off = fdt_subnode_offset(fit, images, fdt_name);
|
|
if (fdt_off < 0)
|
|
return -EBADF;
|
|
|
|
fdt_for_each_subnode(hash_off, fit, fdt_off) {
|
|
name = fit_get_name(fit, hash_off, NULL);
|
|
if (!strncmp(name, FIT_HASH_NODENAME,
|
|
strlen(FIT_HASH_NODENAME))) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
return -ENODEV;
|
|
|
|
if (fit_image_hash_get_algo(fit, hash_off, &algo))
|
|
return -EINVAL;
|
|
|
|
if (fit_image_hash_get_value(fit, hash_off, &fit_value,
|
|
&fit_value_len))
|
|
return -EINVAL;
|
|
|
|
if (!strcmp(algo, "sha1"))
|
|
*hash_size = 20;
|
|
else if (!strcmp(algo, "sha256"))
|
|
*hash_size = 32;
|
|
else
|
|
return -EINVAL;
|
|
|
|
/* avoid freed */
|
|
fit_value2 = malloc(fit_value_len);
|
|
if (!fit_value2)
|
|
return -ENOMEM;
|
|
|
|
memcpy(fit_value2, fit_value, fit_value_len);
|
|
*hash = (char *)fit_value2;
|
|
|
|
return 0;
|
|
}
|
|
|
|
ulong fit_image_get_bootable_size(void *fit)
|
|
{
|
|
ulong off[3] = { 0, 0, 0 };
|
|
ulong max_off, load;
|
|
int images, defconf;
|
|
int offset, size;
|
|
|
|
if (fit_get_image_defconf_node(fit, &images, &defconf))
|
|
return -ENODEV;
|
|
|
|
if (!fit_get_load_and_data(fit, images, defconf, FIT_FDT_PROP,
|
|
&load, &offset, &size))
|
|
off[0] = offset + FIT_ALIGN(size);
|
|
|
|
if (!fit_get_load_and_data(fit, images, defconf, FIT_KERNEL_PROP,
|
|
&load, &offset, &size))
|
|
off[1] = offset + FIT_ALIGN(size);
|
|
|
|
if (!fit_get_load_and_data(fit, images, defconf, FIT_RAMDISK_PROP,
|
|
&load, &offset, &size))
|
|
off[2] = offset + FIT_ALIGN(size);
|
|
|
|
max_off = max(off[0], off[1]);
|
|
max_off = max(max_off, off[2]);
|
|
|
|
return FIT_ALIGN(fdt_totalsize(fit)) + max_off;
|
|
}
|
|
|
|
void *fit_image_load_bootables(ulong *size)
|
|
{
|
|
struct blk_desc *dev_desc;
|
|
disk_partition_t part;
|
|
char *part_name;
|
|
int blknum;
|
|
void *fit;
|
|
|
|
dev_desc = rockchip_get_bootdev();
|
|
if (!dev_desc) {
|
|
FIT_I("No dev_desc\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (rockchip_get_boot_mode() == BOOT_MODE_RECOVERY)
|
|
part_name = PART_RECOVERY;
|
|
else
|
|
part_name = PART_BOOT;
|
|
|
|
if (part_get_info_by_name(dev_desc, part_name, &part) < 0) {
|
|
FIT_I("No %s partition\n", part_name);
|
|
return NULL;
|
|
}
|
|
|
|
fit = fit_get_blob(dev_desc, &part);
|
|
if (!fit) {
|
|
FIT_I("No fit blob\n");
|
|
return NULL;
|
|
}
|
|
|
|
*size = fit_image_get_bootable_size(fit);
|
|
if (*size == 0) {
|
|
FIT_I("No bootable image size\n");
|
|
return NULL;
|
|
}
|
|
|
|
blknum = DIV_ROUND_UP(*size, dev_desc->blksz);
|
|
fit = sysmem_alloc(MEM_FIT, blknum * dev_desc->blksz);
|
|
if (!fit)
|
|
return NULL;
|
|
|
|
if (blk_dread(dev_desc, part.start, blknum, fit) != blknum) {
|
|
FIT_I("Failed to load bootable images\n");
|
|
return NULL;
|
|
}
|
|
|
|
return fit;
|
|
}
|
|
|
|
static void verbose_msg(void *fit, int defconf)
|
|
{
|
|
FIT_I("%ssigned, %sconf-required\n",
|
|
fit_is_signed(fit, gd_fdt_blob()) ? "" : "no ",
|
|
fit_is_required(fit, gd_fdt_blob()) ? "" : "no ");
|
|
|
|
printf("DTB: %s\n",
|
|
(char *)fdt_getprop(fit, defconf, FIT_FDT_PROP, NULL));
|
|
}
|
|
|
|
int rockchip_read_fit_dtb(void *fdt_addr, char **hash, int *hash_size)
|
|
{
|
|
struct blk_desc *dev_desc;
|
|
disk_partition_t part;
|
|
char *part_name;
|
|
void *fit;
|
|
ulong rsce;
|
|
int images;
|
|
int defconf;
|
|
int err;
|
|
|
|
dev_desc = rockchip_get_bootdev();
|
|
if (!dev_desc) {
|
|
FIT_I("No dev_desc!\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (rockchip_get_boot_mode() == BOOT_MODE_RECOVERY)
|
|
part_name = PART_RECOVERY;
|
|
else
|
|
part_name = PART_BOOT;
|
|
|
|
err = part_get_info_by_name(dev_desc, part_name, &part);
|
|
if (err < 0) {
|
|
FIT_I("No %s partition\n", part_name);
|
|
return err;
|
|
}
|
|
|
|
fit = fit_get_blob(dev_desc, &part);
|
|
if (!fit) {
|
|
FIT_I("No fdt blob\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (fit_get_image_defconf_node(fit, &images, &defconf)) {
|
|
FIT_I("Failed to get /images and /configures default\n");
|
|
err = -ENODEV;
|
|
goto out;
|
|
}
|
|
|
|
if (fit_image_load_fdt(fit, dev_desc, &part,
|
|
images, defconf, fdt_addr)) {
|
|
FIT_I("Failed to load fdt\n");
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
err = fit_image_get_fdt_hash(fit, images, defconf, hash, hash_size);
|
|
if (err && err != -ENODEV) {
|
|
FIT_I("Failed to get fdt hash\n");
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
verbose_msg(fit, defconf);
|
|
|
|
/* load resource file */
|
|
#ifdef CONFIG_ROCKCHIP_RESOURCE_IMAGE
|
|
err = fit_image_load_resource(fit, dev_desc, &part,
|
|
images, defconf, &rsce);
|
|
if (!err) {
|
|
if (resource_create_ram_list(dev_desc, (void *)rsce))
|
|
FIT_I("Failed to create resource list\n");
|
|
} else if (err == -ENODEV) {
|
|
FIT_I("No resource file\n");
|
|
} else {
|
|
FIT_I("Failed to load resource file\n");
|
|
}
|
|
#endif
|
|
out:
|
|
free(fit);
|
|
|
|
return err;
|
|
}
|