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