uboot/arch/arm/mach-tegra/cboot.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2016-2018, NVIDIA CORPORATION.
   4 */
   5
   6#include <common.h>
   7#include <env.h>
   8#include <fdt_support.h>
   9#include <fdtdec.h>
  10#include <hang.h>
  11#include <init.h>
  12#include <log.h>
  13#include <malloc.h>
  14#include <net.h>
  15#include <stdlib.h>
  16#include <string.h>
  17
  18#include <linux/ctype.h>
  19#include <linux/sizes.h>
  20
  21#include <asm/arch/tegra.h>
  22#include <asm/arch-tegra/cboot.h>
  23#include <asm/armv8/mmu.h>
  24
  25/*
  26 * Size of a region that's large enough to hold the relocated U-Boot and all
  27 * other allocations made around it (stack, heap, page tables, etc.)
  28 * In practice, running "bdinfo" at the shell prompt, the stack reaches about
  29 * 5MB from the address selected for ram_top as of the time of writing,
  30 * so a 16MB region should be plenty.
  31 */
  32#define MIN_USABLE_RAM_SIZE SZ_16M
  33/*
  34 * The amount of space we expect to require for stack usage. Used to validate
  35 * that all reservations fit into the region selected for the relocation target
  36 */
  37#define MIN_USABLE_STACK_SIZE SZ_1M
  38
  39DECLARE_GLOBAL_DATA_PTR;
  40
  41extern struct mm_region tegra_mem_map[];
  42
  43/*
  44 * These variables are written to before relocation, and hence cannot be
  45 * in.bss, since .bss overlaps the DTB that's appended to the U-Boot binary.
  46 * The section attribute forces this into .data and avoids this issue. This
  47 * also has the nice side-effect of the content being valid after relocation.
  48 */
  49
  50/* The number of valid entries in ram_banks[] */
  51static int ram_bank_count __attribute__((section(".data")));
  52
  53/*
  54 * The usable top-of-RAM for U-Boot. This is both:
  55 * a) Below 4GB to avoid issues with peripherals that use 32-bit addressing.
  56 * b) At the end of a region that has enough space to hold the relocated U-Boot
  57 *    and all other allocations made around it (stack, heap, page tables, etc.)
  58 */
  59static u64 ram_top __attribute__((section(".data")));
  60/* The base address of the region of RAM that ends at ram_top */
  61static u64 region_base __attribute__((section(".data")));
  62
  63/*
  64 * Explicitly put this in the .data section because it is written before the
  65 * .bss section is zeroed out but it needs to persist.
  66 */
  67unsigned long cboot_boot_x0 __attribute__((section(".data")));
  68
  69void cboot_save_boot_params(unsigned long x0, unsigned long x1,
  70                            unsigned long x2, unsigned long x3)
  71{
  72        cboot_boot_x0 = x0;
  73}
  74
  75int cboot_dram_init(void)
  76{
  77        unsigned int na, ns;
  78        const void *cboot_blob = (void *)cboot_boot_x0;
  79        int node, len, i;
  80        const u32 *prop;
  81
  82        if (!cboot_blob)
  83                return -EINVAL;
  84
  85        na = fdtdec_get_uint(cboot_blob, 0, "#address-cells", 2);
  86        ns = fdtdec_get_uint(cboot_blob, 0, "#size-cells", 2);
  87
  88        node = fdt_path_offset(cboot_blob, "/memory");
  89        if (node < 0) {
  90                pr_err("Can't find /memory node in cboot DTB");
  91                hang();
  92        }
  93        prop = fdt_getprop(cboot_blob, node, "reg", &len);
  94        if (!prop) {
  95                pr_err("Can't find /memory/reg property in cboot DTB");
  96                hang();
  97        }
  98
  99        /* Calculate the true # of base/size pairs to read */
 100        len /= 4;               /* Convert bytes to number of cells */
 101        len /= (na + ns);       /* Convert cells to number of banks */
 102        if (len > CONFIG_NR_DRAM_BANKS)
 103                len = CONFIG_NR_DRAM_BANKS;
 104
 105        /* Parse the /memory node, and save useful entries */
 106        gd->ram_size = 0;
 107        ram_bank_count = 0;
 108        for (i = 0; i < len; i++) {
 109                u64 bank_start, bank_end, bank_size, usable_bank_size;
 110
 111                /* Extract raw memory region data from DTB */
 112                bank_start = fdt_read_number(prop, na);
 113                prop += na;
 114                bank_size = fdt_read_number(prop, ns);
 115                prop += ns;
 116                gd->ram_size += bank_size;
 117                bank_end = bank_start + bank_size;
 118                debug("Bank %d: %llx..%llx (+%llx)\n", i,
 119                      bank_start, bank_end, bank_size);
 120
 121                /*
 122                 * Align the bank to MMU section size. This is not strictly
 123                 * necessary, since the translation table construction code
 124                 * handles page granularity without issue. However, aligning
 125                 * the MMU entries reduces the size and number of levels in the
 126                 * page table, so is worth it.
 127                 */
 128                bank_start = ROUND(bank_start, SZ_2M);
 129                bank_end = bank_end & ~(SZ_2M - 1);
 130                bank_size = bank_end - bank_start;
 131                debug("  aligned: %llx..%llx (+%llx)\n",
 132                      bank_start, bank_end, bank_size);
 133                if (bank_end <= bank_start)
 134                        continue;
 135
 136                /* Record data used to create MMU translation tables */
 137                ram_bank_count++;
 138                /* Index below is deliberately 1-based to skip MMIO entry */
 139                tegra_mem_map[ram_bank_count].virt = bank_start;
 140                tegra_mem_map[ram_bank_count].phys = bank_start;
 141                tegra_mem_map[ram_bank_count].size = bank_size;
 142                tegra_mem_map[ram_bank_count].attrs =
 143                        PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE;
 144
 145                /* Determine best bank to relocate U-Boot into */
 146                if (bank_end > SZ_4G)
 147                        bank_end = SZ_4G;
 148                debug("  end  %llx (usable)\n", bank_end);
 149                usable_bank_size = bank_end - bank_start;
 150                debug("  size %llx (usable)\n", usable_bank_size);
 151                if ((usable_bank_size >= MIN_USABLE_RAM_SIZE) &&
 152                    (bank_end > ram_top)) {
 153                        ram_top = bank_end;
 154                        region_base = bank_start;
 155                        debug("ram top now %llx\n", ram_top);
 156                }
 157        }
 158
 159        /* Ensure memory map contains the desired sentinel entry */
 160        tegra_mem_map[ram_bank_count + 1].virt = 0;
 161        tegra_mem_map[ram_bank_count + 1].phys = 0;
 162        tegra_mem_map[ram_bank_count + 1].size = 0;
 163        tegra_mem_map[ram_bank_count + 1].attrs = 0;
 164
 165        /* Error out if a relocation target couldn't be found */
 166        if (!ram_top) {
 167                pr_err("Can't find a usable RAM top");
 168                hang();
 169        }
 170
 171        return 0;
 172}
 173
 174int cboot_dram_init_banksize(void)
 175{
 176        int i;
 177
 178        if (ram_bank_count == 0)
 179                return -EINVAL;
 180
 181        if ((gd->start_addr_sp - region_base) < MIN_USABLE_STACK_SIZE) {
 182                pr_err("Reservations exceed chosen region size");
 183                hang();
 184        }
 185
 186        for (i = 0; i < ram_bank_count; i++) {
 187                gd->bd->bi_dram[i].start = tegra_mem_map[1 + i].virt;
 188                gd->bd->bi_dram[i].size = tegra_mem_map[1 + i].size;
 189        }
 190
 191#ifdef CONFIG_PCI
 192        gd->pci_ram_top = ram_top;
 193#endif
 194
 195        return 0;
 196}
 197
 198ulong cboot_get_usable_ram_top(ulong total_size)
 199{
 200        return ram_top;
 201}
 202
 203/*
 204 * The following few functions run late during the boot process and dynamically
 205 * calculate the load address of various binaries. To keep track of multiple
 206 * allocations, some writable list of RAM banks must be used. tegra_mem_map[]
 207 * is used for this purpose to avoid making yet another copy of the list of RAM
 208 * banks. This is safe because tegra_mem_map[] is only used once during very
 209 * early boot to create U-Boot's page tables, long before this code runs. If
 210 * this assumption becomes invalid later, we can just fix the code to copy the
 211 * list of RAM banks into some private data structure before running.
 212 */
 213
 214static char *gen_varname(const char *var, const char *ext)
 215{
 216        size_t len_var = strlen(var);
 217        size_t len_ext = strlen(ext);
 218        size_t len = len_var + len_ext + 1;
 219        char *varext = malloc(len);
 220
 221        if (!varext)
 222                return 0;
 223        strcpy(varext, var);
 224        strcpy(varext + len_var, ext);
 225        return varext;
 226}
 227
 228static void mark_ram_allocated(int bank, u64 allocated_start, u64 allocated_end)
 229{
 230        u64 bank_start = tegra_mem_map[bank].virt;
 231        u64 bank_size = tegra_mem_map[bank].size;
 232        u64 bank_end = bank_start + bank_size;
 233        bool keep_front = allocated_start != bank_start;
 234        bool keep_tail = allocated_end != bank_end;
 235
 236        if (keep_front && keep_tail) {
 237                /*
 238                 * There are CONFIG_NR_DRAM_BANKS DRAM entries in the array,
 239                 * starting at index 1 (index 0 is MMIO). So, we are at DRAM
 240                 * entry "bank" not "bank - 1" as for a typical 0-base array.
 241                 * The number of remaining DRAM entries is therefore
 242                 * "CONFIG_NR_DRAM_BANKS - bank". We want to duplicate the
 243                 * current entry and shift up the remaining entries, dropping
 244                 * the last one. Thus, we must copy one fewer entry than the
 245                 * number remaining.
 246                 */
 247                memmove(&tegra_mem_map[bank + 1], &tegra_mem_map[bank],
 248                        CONFIG_NR_DRAM_BANKS - bank - 1);
 249                tegra_mem_map[bank].size = allocated_start - bank_start;
 250                bank++;
 251                tegra_mem_map[bank].virt = allocated_end;
 252                tegra_mem_map[bank].phys = allocated_end;
 253                tegra_mem_map[bank].size = bank_end - allocated_end;
 254        } else if (keep_front) {
 255                tegra_mem_map[bank].size = allocated_start - bank_start;
 256        } else if (keep_tail) {
 257                tegra_mem_map[bank].virt = allocated_end;
 258                tegra_mem_map[bank].phys = allocated_end;
 259                tegra_mem_map[bank].size = bank_end - allocated_end;
 260        } else {
 261                /*
 262                 * We could move all subsequent banks down in the array but
 263                 * that's not necessary for subsequent allocations to work, so
 264                 * we skip doing so.
 265                 */
 266                tegra_mem_map[bank].size = 0;
 267        }
 268}
 269
 270static void reserve_ram(u64 start, u64 size)
 271{
 272        int bank;
 273        u64 end = start + size;
 274
 275        for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
 276                u64 bank_start = tegra_mem_map[bank].virt;
 277                u64 bank_size = tegra_mem_map[bank].size;
 278                u64 bank_end = bank_start + bank_size;
 279
 280                if (end <= bank_start || start > bank_end)
 281                        continue;
 282                mark_ram_allocated(bank, start, end);
 283                break;
 284        }
 285}
 286
 287static u64 alloc_ram(u64 size, u64 align, u64 offset)
 288{
 289        int bank;
 290
 291        for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
 292                u64 bank_start = tegra_mem_map[bank].virt;
 293                u64 bank_size = tegra_mem_map[bank].size;
 294                u64 bank_end = bank_start + bank_size;
 295                u64 allocated = ROUND(bank_start, align) + offset;
 296                u64 allocated_end = allocated + size;
 297
 298                if (allocated_end > bank_end)
 299                        continue;
 300                mark_ram_allocated(bank, allocated, allocated_end);
 301                return allocated;
 302        }
 303        return 0;
 304}
 305
 306static void set_calculated_aliases(char *aliases, u64 address)
 307{
 308        char *tmp, *alias;
 309        int err;
 310
 311        aliases = strdup(aliases);
 312        if (!aliases) {
 313                pr_err("strdup(aliases) failed");
 314                return;
 315        }
 316
 317        tmp = aliases;
 318        while (true) {
 319                alias = strsep(&tmp, " ");
 320                if (!alias)
 321                        break;
 322                debug("%s: alias: %s\n", __func__, alias);
 323                err = env_set_hex(alias, address);
 324                if (err)
 325                        pr_err("Could not set %s\n", alias);
 326        }
 327
 328        free(aliases);
 329}
 330
 331static void set_calculated_env_var(const char *var)
 332{
 333        char *var_size;
 334        char *var_align;
 335        char *var_offset;
 336        char *var_aliases;
 337        u64 size;
 338        u64 align;
 339        u64 offset;
 340        char *aliases;
 341        u64 address;
 342        int err;
 343
 344        var_size = gen_varname(var, "_size");
 345        if (!var_size)
 346                return;
 347        var_align = gen_varname(var, "_align");
 348        if (!var_align)
 349                goto out_free_var_size;
 350        var_offset = gen_varname(var, "_offset");
 351        if (!var_offset)
 352                goto out_free_var_align;
 353        var_aliases = gen_varname(var, "_aliases");
 354        if (!var_aliases)
 355                goto out_free_var_offset;
 356
 357        size = env_get_hex(var_size, 0);
 358        if (!size) {
 359                pr_err("%s not set or zero\n", var_size);
 360                goto out_free_var_aliases;
 361        }
 362        align = env_get_hex(var_align, 1);
 363        /* Handle extant variables, but with a value of 0 */
 364        if (!align)
 365                align = 1;
 366        offset = env_get_hex(var_offset, 0);
 367        aliases = env_get(var_aliases);
 368
 369        debug("%s: Calc var %s; size=%llx, align=%llx, offset=%llx\n",
 370              __func__, var, size, align, offset);
 371        if (aliases)
 372                debug("%s: Aliases: %s\n", __func__, aliases);
 373
 374        address = alloc_ram(size, align, offset);
 375        if (!address) {
 376                pr_err("Could not allocate %s\n", var);
 377                goto out_free_var_aliases;
 378        }
 379        debug("%s: Address %llx\n", __func__, address);
 380
 381        err = env_set_hex(var, address);
 382        if (err)
 383                pr_err("Could not set %s\n", var);
 384        if (aliases)
 385                set_calculated_aliases(aliases, address);
 386
 387out_free_var_aliases:
 388        free(var_aliases);
 389out_free_var_offset:
 390        free(var_offset);
 391out_free_var_align:
 392        free(var_align);
 393out_free_var_size:
 394        free(var_size);
 395}
 396
 397#ifdef DEBUG
 398static void dump_ram_banks(void)
 399{
 400        int bank;
 401
 402        for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
 403                u64 bank_start = tegra_mem_map[bank].virt;
 404                u64 bank_size = tegra_mem_map[bank].size;
 405                u64 bank_end = bank_start + bank_size;
 406
 407                if (!bank_size)
 408                        continue;
 409                printf("%d: %010llx..%010llx (+%010llx)\n", bank - 1,
 410                       bank_start, bank_end, bank_size);
 411        }
 412}
 413#endif
 414
 415static void set_calculated_env_vars(void)
 416{
 417        char *vars, *tmp, *var;
 418
 419#ifdef DEBUG
 420        printf("RAM banks before any calculated env. var.s:\n");
 421        dump_ram_banks();
 422#endif
 423
 424        reserve_ram(cboot_boot_x0, fdt_totalsize(cboot_boot_x0));
 425
 426#ifdef DEBUG
 427        printf("RAM after reserving cboot DTB:\n");
 428        dump_ram_banks();
 429#endif
 430
 431        vars = env_get("calculated_vars");
 432        if (!vars) {
 433                debug("%s: No env var calculated_vars\n", __func__);
 434                return;
 435        }
 436
 437        vars = strdup(vars);
 438        if (!vars) {
 439                pr_err("strdup(calculated_vars) failed");
 440                return;
 441        }
 442
 443        tmp = vars;
 444        while (true) {
 445                var = strsep(&tmp, " ");
 446                if (!var)
 447                        break;
 448                debug("%s: var: %s\n", __func__, var);
 449                set_calculated_env_var(var);
 450#ifdef DEBUG
 451                printf("RAM banks after allocating %s:\n", var);
 452                dump_ram_banks();
 453#endif
 454        }
 455
 456        free(vars);
 457}
 458
 459static int set_fdt_addr(void)
 460{
 461        int ret;
 462
 463        ret = env_set_hex("fdt_addr", cboot_boot_x0);
 464        if (ret) {
 465                printf("Failed to set fdt_addr to point at DTB: %d\n", ret);
 466                return ret;
 467        }
 468
 469        return 0;
 470}
 471
 472/*
 473 * Attempt to use /chosen/nvidia,ether-mac in the cboot DTB to U-Boot's
 474 * ethaddr environment variable if possible.
 475 */
 476static int cboot_get_ethaddr_legacy(const void *fdt, uint8_t mac[ETH_ALEN])
 477{
 478        const char *const properties[] = {
 479                "nvidia,ethernet-mac",
 480                "nvidia,ether-mac",
 481        };
 482        const char *prop;
 483        unsigned int i;
 484        int node, len;
 485
 486        node = fdt_path_offset(fdt, "/chosen");
 487        if (node < 0) {
 488                printf("Can't find /chosen node in cboot DTB\n");
 489                return node;
 490        }
 491
 492        for (i = 0; i < ARRAY_SIZE(properties); i++) {
 493                prop = fdt_getprop(fdt, node, properties[i], &len);
 494                if (prop)
 495                        break;
 496        }
 497
 498        if (!prop) {
 499                printf("Can't find Ethernet MAC address in cboot DTB\n");
 500                return -ENOENT;
 501        }
 502
 503        string_to_enetaddr(prop, mac);
 504
 505        if (!is_valid_ethaddr(mac)) {
 506                printf("Invalid MAC address: %s\n", prop);
 507                return -EINVAL;
 508        }
 509
 510        debug("Legacy MAC address: %pM\n", mac);
 511
 512        return 0;
 513}
 514
 515int cboot_get_ethaddr(const void *fdt, uint8_t mac[ETH_ALEN])
 516{
 517        int node, len, err = 0;
 518        const uchar *prop;
 519        const char *path;
 520
 521        path = fdt_get_alias(fdt, "ethernet");
 522        if (!path) {
 523                err = -ENOENT;
 524                goto out;
 525        }
 526
 527        debug("ethernet alias found: %s\n", path);
 528
 529        node = fdt_path_offset(fdt, path);
 530        if (node < 0) {
 531                err = -ENOENT;
 532                goto out;
 533        }
 534
 535        prop = fdt_getprop(fdt, node, "local-mac-address", &len);
 536        if (!prop) {
 537                err = -ENOENT;
 538                goto out;
 539        }
 540
 541        if (len != ETH_ALEN) {
 542                err = -EINVAL;
 543                goto out;
 544        }
 545
 546        debug("MAC address: %pM\n", prop);
 547        memcpy(mac, prop, ETH_ALEN);
 548
 549out:
 550        if (err < 0)
 551                err = cboot_get_ethaddr_legacy(fdt, mac);
 552
 553        return err;
 554}
 555
 556static char *strip(const char *ptr)
 557{
 558        const char *end;
 559
 560        while (*ptr && isblank(*ptr))
 561                ptr++;
 562
 563        /* empty string */
 564        if (*ptr == '\0')
 565                return strdup(ptr);
 566
 567        end = ptr;
 568
 569        while (end[1])
 570                end++;
 571
 572        while (isblank(*end))
 573                end--;
 574
 575        return strndup(ptr, end - ptr + 1);
 576}
 577
 578static char *cboot_get_bootargs(const void *fdt)
 579{
 580        const char *args;
 581        int offset, len;
 582
 583        offset = fdt_path_offset(fdt, "/chosen");
 584        if (offset < 0)
 585                return NULL;
 586
 587        args = fdt_getprop(fdt, offset, "bootargs", &len);
 588        if (!args)
 589                return NULL;
 590
 591        return strip(args);
 592}
 593
 594int cboot_late_init(void)
 595{
 596        const void *fdt = (const void *)cboot_boot_x0;
 597        uint8_t mac[ETH_ALEN];
 598        char *bootargs;
 599        int err;
 600
 601        set_calculated_env_vars();
 602        /*
 603         * Ignore errors here; the value may not be used depending on
 604         * extlinux.conf or boot script content.
 605         */
 606        set_fdt_addr();
 607
 608        /* Ignore errors here; not all cases care about Ethernet addresses */
 609        err = cboot_get_ethaddr(fdt, mac);
 610        if (!err) {
 611                void *blob = (void *)gd->fdt_blob;
 612
 613                err = fdtdec_set_ethernet_mac_address(blob, mac, sizeof(mac));
 614                if (err < 0)
 615                        printf("failed to set MAC address %pM: %d\n", mac, err);
 616        }
 617
 618        bootargs = cboot_get_bootargs(fdt);
 619        if (bootargs) {
 620                env_set("cbootargs", bootargs);
 621                free(bootargs);
 622        }
 623
 624        return 0;
 625}
 626