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