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