uboot/common/image-fdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2013, Google Inc.
   4 *
   5 * (C) Copyright 2008 Semihalf
   6 *
   7 * (C) Copyright 2000-2006
   8 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
   9 */
  10
  11#include <common.h>
  12#include <fdt_support.h>
  13#include <fdtdec.h>
  14#include <env.h>
  15#include <errno.h>
  16#include <image.h>
  17#include <linux/libfdt.h>
  18#include <mapmem.h>
  19#include <asm/io.h>
  20#include <tee/optee.h>
  21
  22#ifndef CONFIG_SYS_FDT_PAD
  23#define CONFIG_SYS_FDT_PAD 0x3000
  24#endif
  25
  26/* adding a ramdisk needs 0x44 bytes in version 2008.10 */
  27#define FDT_RAMDISK_OVERHEAD    0x80
  28
  29DECLARE_GLOBAL_DATA_PTR;
  30
  31static void fdt_error(const char *msg)
  32{
  33        puts("ERROR: ");
  34        puts(msg);
  35        puts(" - must RESET the board to recover.\n");
  36}
  37
  38#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT)
  39static const image_header_t *image_get_fdt(ulong fdt_addr)
  40{
  41        const image_header_t *fdt_hdr = map_sysmem(fdt_addr, 0);
  42
  43        image_print_contents(fdt_hdr);
  44
  45        puts("   Verifying Checksum ... ");
  46        if (!image_check_hcrc(fdt_hdr)) {
  47                fdt_error("fdt header checksum invalid");
  48                return NULL;
  49        }
  50
  51        if (!image_check_dcrc(fdt_hdr)) {
  52                fdt_error("fdt checksum invalid");
  53                return NULL;
  54        }
  55        puts("OK\n");
  56
  57        if (!image_check_type(fdt_hdr, IH_TYPE_FLATDT)) {
  58                fdt_error("uImage is not a fdt");
  59                return NULL;
  60        }
  61        if (image_get_comp(fdt_hdr) != IH_COMP_NONE) {
  62                fdt_error("uImage is compressed");
  63                return NULL;
  64        }
  65        if (fdt_check_header((void *)image_get_data(fdt_hdr)) != 0) {
  66                fdt_error("uImage data is not a fdt");
  67                return NULL;
  68        }
  69        return fdt_hdr;
  70}
  71#endif
  72
  73static void boot_fdt_reserve_region(struct lmb *lmb, uint64_t addr,
  74                                    uint64_t size)
  75{
  76        long ret;
  77
  78        ret = lmb_reserve(lmb, addr, size);
  79        if (ret >= 0) {
  80                debug("   reserving fdt memory region: addr=%llx size=%llx\n",
  81                      (unsigned long long)addr, (unsigned long long)size);
  82        } else {
  83                puts("ERROR: reserving fdt memory region failed ");
  84                printf("(addr=%llx size=%llx)\n",
  85                       (unsigned long long)addr, (unsigned long long)size);
  86        }
  87}
  88
  89/**
  90 * boot_fdt_add_mem_rsv_regions - Mark the memreserve and reserved-memory
  91 * sections as unusable
  92 * @lmb: pointer to lmb handle, will be used for memory mgmt
  93 * @fdt_blob: pointer to fdt blob base address
  94 *
  95 * Adds the and reserved-memorymemreserve regions in the dtb to the lmb block.
  96 * Adding the memreserve regions prevents u-boot from using them to store the
  97 * initrd or the fdt blob.
  98 */
  99void boot_fdt_add_mem_rsv_regions(struct lmb *lmb, void *fdt_blob)
 100{
 101        uint64_t addr, size;
 102        int i, total, ret;
 103        int nodeoffset, subnode;
 104        struct fdt_resource res;
 105
 106        if (fdt_check_header(fdt_blob) != 0)
 107                return;
 108
 109        /* process memreserve sections */
 110        total = fdt_num_mem_rsv(fdt_blob);
 111        for (i = 0; i < total; i++) {
 112                if (fdt_get_mem_rsv(fdt_blob, i, &addr, &size) != 0)
 113                        continue;
 114                boot_fdt_reserve_region(lmb, addr, size);
 115        }
 116
 117        /* process reserved-memory */
 118        nodeoffset = fdt_subnode_offset(fdt_blob, 0, "reserved-memory");
 119        if (nodeoffset >= 0) {
 120                subnode = fdt_first_subnode(fdt_blob, nodeoffset);
 121                while (subnode >= 0) {
 122                        /* check if this subnode has a reg property */
 123                        ret = fdt_get_resource(fdt_blob, subnode, "reg", 0,
 124                                               &res);
 125                        if (!ret) {
 126                                addr = res.start;
 127                                size = res.end - res.start + 1;
 128                                boot_fdt_reserve_region(lmb, addr, size);
 129                        }
 130
 131                        subnode = fdt_next_subnode(fdt_blob, subnode);
 132                }
 133        }
 134}
 135
 136/**
 137 * boot_relocate_fdt - relocate flat device tree
 138 * @lmb: pointer to lmb handle, will be used for memory mgmt
 139 * @of_flat_tree: pointer to a char* variable, will hold fdt start address
 140 * @of_size: pointer to a ulong variable, will hold fdt length
 141 *
 142 * boot_relocate_fdt() allocates a region of memory within the bootmap and
 143 * relocates the of_flat_tree into that region, even if the fdt is already in
 144 * the bootmap.  It also expands the size of the fdt by CONFIG_SYS_FDT_PAD
 145 * bytes.
 146 *
 147 * of_flat_tree and of_size are set to final (after relocation) values
 148 *
 149 * returns:
 150 *      0 - success
 151 *      1 - failure
 152 */
 153int boot_relocate_fdt(struct lmb *lmb, char **of_flat_tree, ulong *of_size)
 154{
 155        void    *fdt_blob = *of_flat_tree;
 156        void    *of_start = NULL;
 157        char    *fdt_high;
 158        ulong   of_len = 0;
 159        int     err;
 160        int     disable_relocation = 0;
 161
 162        /* nothing to do */
 163        if (*of_size == 0)
 164                return 0;
 165
 166        if (fdt_check_header(fdt_blob) != 0) {
 167                fdt_error("image is not a fdt");
 168                goto error;
 169        }
 170
 171        /* position on a 4K boundary before the alloc_current */
 172        /* Pad the FDT by a specified amount */
 173        of_len = *of_size + CONFIG_SYS_FDT_PAD;
 174
 175        /* If fdt_high is set use it to select the relocation address */
 176        fdt_high = env_get("fdt_high");
 177        if (fdt_high) {
 178                void *desired_addr = (void *)simple_strtoul(fdt_high, NULL, 16);
 179
 180                if (((ulong) desired_addr) == ~0UL) {
 181                        /* All ones means use fdt in place */
 182                        of_start = fdt_blob;
 183                        lmb_reserve(lmb, (ulong)of_start, of_len);
 184                        disable_relocation = 1;
 185                } else if (desired_addr) {
 186                        of_start =
 187                            (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000,
 188                                                           (ulong)desired_addr);
 189                        if (of_start == NULL) {
 190                                puts("Failed using fdt_high value for Device Tree");
 191                                goto error;
 192                        }
 193                } else {
 194                        of_start =
 195                            (void *)(ulong) lmb_alloc(lmb, of_len, 0x1000);
 196                }
 197        } else {
 198                of_start =
 199                    (void *)(ulong) lmb_alloc_base(lmb, of_len, 0x1000,
 200                                                   env_get_bootm_mapsize()
 201                                                   + env_get_bootm_low());
 202        }
 203
 204        if (of_start == NULL) {
 205                puts("device tree - allocation error\n");
 206                goto error;
 207        }
 208
 209        if (disable_relocation) {
 210                /*
 211                 * We assume there is space after the existing fdt to use
 212                 * for padding
 213                 */
 214                fdt_set_totalsize(of_start, of_len);
 215                printf("   Using Device Tree in place at %p, end %p\n",
 216                       of_start, of_start + of_len - 1);
 217        } else {
 218                debug("## device tree at %p ... %p (len=%ld [0x%lX])\n",
 219                      fdt_blob, fdt_blob + *of_size - 1, of_len, of_len);
 220
 221                printf("   Loading Device Tree to %p, end %p ... ",
 222                       of_start, of_start + of_len - 1);
 223
 224                err = fdt_open_into(fdt_blob, of_start, of_len);
 225                if (err != 0) {
 226                        fdt_error("fdt move failed");
 227                        goto error;
 228                }
 229                puts("OK\n");
 230        }
 231
 232        *of_flat_tree = of_start;
 233        *of_size = of_len;
 234
 235        if (CONFIG_IS_ENABLED(CMD_FDT))
 236                set_working_fdt_addr(map_to_sysmem(*of_flat_tree));
 237        return 0;
 238
 239error:
 240        return 1;
 241}
 242
 243/**
 244 * boot_get_fdt - main fdt handling routine
 245 * @argc: command argument count
 246 * @argv: command argument list
 247 * @arch: architecture (IH_ARCH_...)
 248 * @images: pointer to the bootm images structure
 249 * @of_flat_tree: pointer to a char* variable, will hold fdt start address
 250 * @of_size: pointer to a ulong variable, will hold fdt length
 251 *
 252 * boot_get_fdt() is responsible for finding a valid flat device tree image.
 253 * Curently supported are the following ramdisk sources:
 254 *      - multicomponent kernel/ramdisk image,
 255 *      - commandline provided address of decicated ramdisk image.
 256 *
 257 * returns:
 258 *     0, if fdt image was found and valid, or skipped
 259 *     of_flat_tree and of_size are set to fdt start address and length if
 260 *     fdt image is found and valid
 261 *
 262 *     1, if fdt image is found but corrupted
 263 *     of_flat_tree and of_size are set to 0 if no fdt exists
 264 */
 265int boot_get_fdt(int flag, int argc, char * const argv[], uint8_t arch,
 266                bootm_headers_t *images, char **of_flat_tree, ulong *of_size)
 267{
 268#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT)
 269        const image_header_t *fdt_hdr;
 270        ulong           load, load_end;
 271        ulong           image_start, image_data, image_end;
 272#endif
 273        ulong           img_addr;
 274        ulong           fdt_addr;
 275        char            *fdt_blob = NULL;
 276        void            *buf;
 277#if CONFIG_IS_ENABLED(FIT)
 278        const char      *fit_uname_config = images->fit_uname_cfg;
 279        const char      *fit_uname_fdt = NULL;
 280        ulong           default_addr;
 281        int             fdt_noffset;
 282#endif
 283        const char *select = NULL;
 284
 285        *of_flat_tree = NULL;
 286        *of_size = 0;
 287
 288        img_addr = (argc == 0) ? load_addr : simple_strtoul(argv[0], NULL, 16);
 289        buf = map_sysmem(img_addr, 0);
 290
 291        if (argc > 2)
 292                select = argv[2];
 293        if (select || genimg_has_config(images)) {
 294#if CONFIG_IS_ENABLED(FIT)
 295                if (select) {
 296                        /*
 297                         * If the FDT blob comes from the FIT image and the
 298                         * FIT image address is omitted in the command line
 299                         * argument, try to use ramdisk or os FIT image
 300                         * address or default load address.
 301                         */
 302                        if (images->fit_uname_rd)
 303                                default_addr = (ulong)images->fit_hdr_rd;
 304                        else if (images->fit_uname_os)
 305                                default_addr = (ulong)images->fit_hdr_os;
 306                        else
 307                                default_addr = load_addr;
 308
 309                        if (fit_parse_conf(select, default_addr,
 310                                           &fdt_addr, &fit_uname_config)) {
 311                                debug("*  fdt: config '%s' from image at 0x%08lx\n",
 312                                      fit_uname_config, fdt_addr);
 313                        } else if (fit_parse_subimage(select, default_addr,
 314                                   &fdt_addr, &fit_uname_fdt)) {
 315                                debug("*  fdt: subimage '%s' from image at 0x%08lx\n",
 316                                      fit_uname_fdt, fdt_addr);
 317                        } else
 318#endif
 319                        {
 320                                fdt_addr = simple_strtoul(select, NULL, 16);
 321                                debug("*  fdt: cmdline image address = 0x%08lx\n",
 322                                      fdt_addr);
 323                        }
 324#if CONFIG_IS_ENABLED(FIT)
 325                } else {
 326                        /* use FIT configuration provided in first bootm
 327                         * command argument
 328                         */
 329                        fdt_addr = map_to_sysmem(images->fit_hdr_os);
 330                        fdt_noffset = fit_get_node_from_config(images,
 331                                                               FIT_FDT_PROP,
 332                                                               fdt_addr);
 333                        if (fdt_noffset == -ENOENT)
 334                                return 0;
 335                        else if (fdt_noffset < 0)
 336                                return 1;
 337                }
 338#endif
 339                debug("## Checking for 'FDT'/'FDT Image' at %08lx\n",
 340                      fdt_addr);
 341
 342                /*
 343                 * Check if there is an FDT image at the
 344                 * address provided in the second bootm argument
 345                 * check image type, for FIT images get a FIT node.
 346                 */
 347                buf = map_sysmem(fdt_addr, 0);
 348                switch (genimg_get_format(buf)) {
 349#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT)
 350                case IMAGE_FORMAT_LEGACY:
 351                        /* verify fdt_addr points to a valid image header */
 352                        printf("## Flattened Device Tree from Legacy Image at %08lx\n",
 353                               fdt_addr);
 354                        fdt_hdr = image_get_fdt(fdt_addr);
 355                        if (!fdt_hdr)
 356                                goto no_fdt;
 357
 358                        /*
 359                         * move image data to the load address,
 360                         * make sure we don't overwrite initial image
 361                         */
 362                        image_start = (ulong)fdt_hdr;
 363                        image_data = (ulong)image_get_data(fdt_hdr);
 364                        image_end = image_get_image_end(fdt_hdr);
 365
 366                        load = image_get_load(fdt_hdr);
 367                        load_end = load + image_get_data_size(fdt_hdr);
 368
 369                        if (load == image_start ||
 370                            load == image_data) {
 371                                fdt_addr = load;
 372                                break;
 373                        }
 374
 375                        if ((load < image_end) && (load_end > image_start)) {
 376                                fdt_error("fdt overwritten");
 377                                goto error;
 378                        }
 379
 380                        debug("   Loading FDT from 0x%08lx to 0x%08lx\n",
 381                              image_data, load);
 382
 383                        memmove((void *)load,
 384                                (void *)image_data,
 385                                image_get_data_size(fdt_hdr));
 386
 387                        fdt_addr = load;
 388                        break;
 389#endif
 390                case IMAGE_FORMAT_FIT:
 391                        /*
 392                         * This case will catch both: new uImage format
 393                         * (libfdt based) and raw FDT blob (also libfdt
 394                         * based).
 395                         */
 396#if CONFIG_IS_ENABLED(FIT)
 397                        /* check FDT blob vs FIT blob */
 398                        if (fit_check_format(buf)) {
 399                                ulong load, len;
 400
 401                                fdt_noffset = boot_get_fdt_fit(images,
 402                                        fdt_addr, &fit_uname_fdt,
 403                                        &fit_uname_config,
 404                                        arch, &load, &len);
 405
 406                                images->fit_hdr_fdt = map_sysmem(fdt_addr, 0);
 407                                images->fit_uname_fdt = fit_uname_fdt;
 408                                images->fit_noffset_fdt = fdt_noffset;
 409                                fdt_addr = load;
 410
 411                                break;
 412                        } else
 413#endif
 414                        {
 415                                /*
 416                                 * FDT blob
 417                                 */
 418                                debug("*  fdt: raw FDT blob\n");
 419                                printf("## Flattened Device Tree blob at %08lx\n",
 420                                       (long)fdt_addr);
 421                        }
 422                        break;
 423                default:
 424                        puts("ERROR: Did not find a cmdline Flattened Device Tree\n");
 425                        goto no_fdt;
 426                }
 427
 428                printf("   Booting using the fdt blob at %#08lx\n", fdt_addr);
 429                fdt_blob = map_sysmem(fdt_addr, 0);
 430        } else if (images->legacy_hdr_valid &&
 431                        image_check_type(&images->legacy_hdr_os_copy,
 432                                         IH_TYPE_MULTI)) {
 433                ulong fdt_data, fdt_len;
 434
 435                /*
 436                 * Now check if we have a legacy multi-component image,
 437                 * get second entry data start address and len.
 438                 */
 439                printf("## Flattened Device Tree from multi component Image at %08lX\n",
 440                       (ulong)images->legacy_hdr_os);
 441
 442                image_multi_getimg(images->legacy_hdr_os, 2, &fdt_data,
 443                                   &fdt_len);
 444                if (fdt_len) {
 445                        fdt_blob = (char *)fdt_data;
 446                        printf("   Booting using the fdt at 0x%p\n", fdt_blob);
 447
 448                        if (fdt_check_header(fdt_blob) != 0) {
 449                                fdt_error("image is not a fdt");
 450                                goto error;
 451                        }
 452
 453                        if (fdt_totalsize(fdt_blob) != fdt_len) {
 454                                fdt_error("fdt size != image size");
 455                                goto error;
 456                        }
 457                } else {
 458                        debug("## No Flattened Device Tree\n");
 459                        goto no_fdt;
 460                }
 461#ifdef CONFIG_ANDROID_BOOT_IMAGE
 462        } else if (genimg_get_format(buf) == IMAGE_FORMAT_ANDROID) {
 463                struct andr_img_hdr *hdr = buf;
 464                ulong fdt_data, fdt_len;
 465
 466                if (!android_image_get_second(hdr, &fdt_data, &fdt_len) &&
 467                    !fdt_check_header((char *)fdt_data)) {
 468                        fdt_blob = (char *)fdt_data;
 469                        if (fdt_totalsize(fdt_blob) != fdt_len)
 470                                goto error;
 471
 472                        debug("## Using FDT in Android image second area\n");
 473                } else {
 474                        fdt_addr = env_get_hex("fdtaddr", 0);
 475                        if (!fdt_addr)
 476                                goto no_fdt;
 477
 478                        fdt_blob = map_sysmem(fdt_addr, 0);
 479                        if (fdt_check_header(fdt_blob))
 480                                goto no_fdt;
 481
 482                        debug("## Using FDT at ${fdtaddr}=Ox%lx\n", fdt_addr);
 483                }
 484#endif
 485        } else {
 486                debug("## No Flattened Device Tree\n");
 487                goto no_fdt;
 488        }
 489
 490        *of_flat_tree = fdt_blob;
 491        *of_size = fdt_totalsize(fdt_blob);
 492        debug("   of_flat_tree at 0x%08lx size 0x%08lx\n",
 493              (ulong)*of_flat_tree, *of_size);
 494
 495        return 0;
 496
 497no_fdt:
 498        debug("Continuing to boot without FDT\n");
 499        return 0;
 500error:
 501        return 1;
 502}
 503
 504/*
 505 * Verify the device tree.
 506 *
 507 * This function is called after all device tree fix-ups have been enacted,
 508 * so that the final device tree can be verified.  The definition of "verified"
 509 * is up to the specific implementation.  However, it generally means that the
 510 * addresses of some of the devices in the device tree are compared with the
 511 * actual addresses at which U-Boot has placed them.
 512 *
 513 * Returns 1 on success, 0 on failure.  If 0 is returned, U-Boot will halt the
 514 * boot process.
 515 */
 516__weak int ft_verify_fdt(void *fdt)
 517{
 518        return 1;
 519}
 520
 521__weak int arch_fixup_fdt(void *blob)
 522{
 523        return 0;
 524}
 525
 526int image_setup_libfdt(bootm_headers_t *images, void *blob,
 527                       int of_size, struct lmb *lmb)
 528{
 529        ulong *initrd_start = &images->initrd_start;
 530        ulong *initrd_end = &images->initrd_end;
 531        int ret = -EPERM;
 532        int fdt_ret;
 533
 534        if (fdt_root(blob) < 0) {
 535                printf("ERROR: root node setup failed\n");
 536                goto err;
 537        }
 538        if (fdt_chosen(blob) < 0) {
 539                printf("ERROR: /chosen node create failed\n");
 540                goto err;
 541        }
 542        if (arch_fixup_fdt(blob) < 0) {
 543                printf("ERROR: arch-specific fdt fixup failed\n");
 544                goto err;
 545        }
 546        /* Update ethernet nodes */
 547        fdt_fixup_ethernet(blob);
 548        if (IMAGE_OF_BOARD_SETUP) {
 549                fdt_ret = ft_board_setup(blob, gd->bd);
 550                if (fdt_ret) {
 551                        printf("ERROR: board-specific fdt fixup failed: %s\n",
 552                               fdt_strerror(fdt_ret));
 553                        goto err;
 554                }
 555        }
 556        if (IMAGE_OF_SYSTEM_SETUP) {
 557                fdt_ret = ft_system_setup(blob, gd->bd);
 558                if (fdt_ret) {
 559                        printf("ERROR: system-specific fdt fixup failed: %s\n",
 560                               fdt_strerror(fdt_ret));
 561                        goto err;
 562                }
 563        }
 564
 565        fdt_ret = optee_copy_fdt_nodes(gd->fdt_blob, blob);
 566        if (fdt_ret) {
 567                printf("ERROR: transfer of optee nodes to new fdt failed: %s\n",
 568                       fdt_strerror(fdt_ret));
 569                goto err;
 570        }
 571
 572        /* Delete the old LMB reservation */
 573        if (lmb)
 574                lmb_free(lmb, (phys_addr_t)(u32)(uintptr_t)blob,
 575                         (phys_size_t)fdt_totalsize(blob));
 576
 577        ret = fdt_shrink_to_minimum(blob, 0);
 578        if (ret < 0)
 579                goto err;
 580        of_size = ret;
 581
 582        if (*initrd_start && *initrd_end) {
 583                of_size += FDT_RAMDISK_OVERHEAD;
 584                fdt_set_totalsize(blob, of_size);
 585        }
 586        /* Create a new LMB reservation */
 587        if (lmb)
 588                lmb_reserve(lmb, (ulong)blob, of_size);
 589
 590        fdt_initrd(blob, *initrd_start, *initrd_end);
 591        if (!ft_verify_fdt(blob))
 592                goto err;
 593
 594#if defined(CONFIG_SOC_KEYSTONE)
 595        if (IMAGE_OF_BOARD_SETUP)
 596                ft_board_setup_ex(blob, gd->bd);
 597#endif
 598
 599        return 0;
 600err:
 601        printf(" - must RESET the board to recover.\n\n");
 602
 603        return ret;
 604}
 605