uboot/cmd/pxe_utils.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2010-2011 Calxeda, Inc.
   4 * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
   5 */
   6
   7#include <common.h>
   8#include <command.h>
   9#include <env.h>
  10#include <image.h>
  11#include <log.h>
  12#include <malloc.h>
  13#include <mapmem.h>
  14#include <lcd.h>
  15#include <net.h>
  16#include <linux/string.h>
  17#include <linux/ctype.h>
  18#include <errno.h>
  19#include <linux/list.h>
  20
  21#include <splash.h>
  22#include <asm/io.h>
  23
  24#include "menu.h"
  25#include "cli.h"
  26
  27#include "pxe_utils.h"
  28
  29#define MAX_TFTP_PATH_LEN 512
  30
  31bool is_pxe;
  32
  33/*
  34 * Convert an ethaddr from the environment to the format used by pxelinux
  35 * filenames based on mac addresses. Convert's ':' to '-', and adds "01-" to
  36 * the beginning of the ethernet address to indicate a hardware type of
  37 * Ethernet. Also converts uppercase hex characters into lowercase, to match
  38 * pxelinux's behavior.
  39 *
  40 * Returns 1 for success, -ENOENT if 'ethaddr' is undefined in the
  41 * environment, or some other value < 0 on error.
  42 */
  43int format_mac_pxe(char *outbuf, size_t outbuf_len)
  44{
  45        uchar ethaddr[6];
  46
  47        if (outbuf_len < 21) {
  48                printf("outbuf is too small (%zd < 21)\n", outbuf_len);
  49
  50                return -EINVAL;
  51        }
  52
  53        if (!eth_env_get_enetaddr_by_index("eth", eth_get_dev_index(), ethaddr))
  54                return -ENOENT;
  55
  56        sprintf(outbuf, "01-%02x-%02x-%02x-%02x-%02x-%02x",
  57                ethaddr[0], ethaddr[1], ethaddr[2],
  58                ethaddr[3], ethaddr[4], ethaddr[5]);
  59
  60        return 1;
  61}
  62
  63/*
  64 * Returns the directory the file specified in the bootfile env variable is
  65 * in. If bootfile isn't defined in the environment, return NULL, which should
  66 * be interpreted as "don't prepend anything to paths".
  67 */
  68static int get_bootfile_path(const char *file_path, char *bootfile_path,
  69                             size_t bootfile_path_size)
  70{
  71        char *bootfile, *last_slash;
  72        size_t path_len = 0;
  73
  74        /* Only syslinux allows absolute paths */
  75        if (file_path[0] == '/' && !is_pxe)
  76                goto ret;
  77
  78        bootfile = from_env("bootfile");
  79
  80        if (!bootfile)
  81                goto ret;
  82
  83        last_slash = strrchr(bootfile, '/');
  84
  85        if (!last_slash)
  86                goto ret;
  87
  88        path_len = (last_slash - bootfile) + 1;
  89
  90        if (bootfile_path_size < path_len) {
  91                printf("bootfile_path too small. (%zd < %zd)\n",
  92                       bootfile_path_size, path_len);
  93
  94                return -1;
  95        }
  96
  97        strncpy(bootfile_path, bootfile, path_len);
  98
  99 ret:
 100        bootfile_path[path_len] = '\0';
 101
 102        return 1;
 103}
 104
 105int (*do_getfile)(struct cmd_tbl *cmdtp, const char *file_path,
 106                  char *file_addr);
 107
 108/*
 109 * As in pxelinux, paths to files referenced from files we retrieve are
 110 * relative to the location of bootfile. get_relfile takes such a path and
 111 * joins it with the bootfile path to get the full path to the target file. If
 112 * the bootfile path is NULL, we use file_path as is.
 113 *
 114 * Returns 1 for success, or < 0 on error.
 115 */
 116static int get_relfile(struct cmd_tbl *cmdtp, const char *file_path,
 117                       unsigned long file_addr)
 118{
 119        size_t path_len;
 120        char relfile[MAX_TFTP_PATH_LEN + 1];
 121        char addr_buf[18];
 122        int err;
 123
 124        err = get_bootfile_path(file_path, relfile, sizeof(relfile));
 125
 126        if (err < 0)
 127                return err;
 128
 129        path_len = strlen(file_path);
 130        path_len += strlen(relfile);
 131
 132        if (path_len > MAX_TFTP_PATH_LEN) {
 133                printf("Base path too long (%s%s)\n", relfile, file_path);
 134
 135                return -ENAMETOOLONG;
 136        }
 137
 138        strcat(relfile, file_path);
 139
 140        printf("Retrieving file: %s\n", relfile);
 141
 142        sprintf(addr_buf, "%lx", file_addr);
 143
 144        return do_getfile(cmdtp, relfile, addr_buf);
 145}
 146
 147/*
 148 * Retrieve the file at 'file_path' to the locate given by 'file_addr'. If
 149 * 'bootfile' was specified in the environment, the path to bootfile will be
 150 * prepended to 'file_path' and the resulting path will be used.
 151 *
 152 * Returns 1 on success, or < 0 for error.
 153 */
 154int get_pxe_file(struct cmd_tbl *cmdtp, const char *file_path,
 155                 unsigned long file_addr)
 156{
 157        unsigned long config_file_size;
 158        char *tftp_filesize;
 159        int err;
 160        char *buf;
 161
 162        err = get_relfile(cmdtp, file_path, file_addr);
 163
 164        if (err < 0)
 165                return err;
 166
 167        /*
 168         * the file comes without a NUL byte at the end, so find out its size
 169         * and add the NUL byte.
 170         */
 171        tftp_filesize = from_env("filesize");
 172
 173        if (!tftp_filesize)
 174                return -ENOENT;
 175
 176        if (strict_strtoul(tftp_filesize, 16, &config_file_size) < 0)
 177                return -EINVAL;
 178
 179        buf = map_sysmem(file_addr + config_file_size, 1);
 180        *buf = '\0';
 181        unmap_sysmem(buf);
 182
 183        return 1;
 184}
 185
 186#define PXELINUX_DIR "pxelinux.cfg/"
 187
 188/*
 189 * Retrieves a file in the 'pxelinux.cfg' folder. Since this uses get_pxe_file
 190 * to do the hard work, the location of the 'pxelinux.cfg' folder is generated
 191 * from the bootfile path, as described above.
 192 *
 193 * Returns 1 on success or < 0 on error.
 194 */
 195int get_pxelinux_path(struct cmd_tbl *cmdtp, const char *file,
 196                      unsigned long pxefile_addr_r)
 197{
 198        size_t base_len = strlen(PXELINUX_DIR);
 199        char path[MAX_TFTP_PATH_LEN + 1];
 200
 201        if (base_len + strlen(file) > MAX_TFTP_PATH_LEN) {
 202                printf("path (%s%s) too long, skipping\n",
 203                       PXELINUX_DIR, file);
 204                return -ENAMETOOLONG;
 205        }
 206
 207        sprintf(path, PXELINUX_DIR "%s", file);
 208
 209        return get_pxe_file(cmdtp, path, pxefile_addr_r);
 210}
 211
 212/*
 213 * Wrapper to make it easier to store the file at file_path in the location
 214 * specified by envaddr_name. file_path will be joined to the bootfile path,
 215 * if any is specified.
 216 *
 217 * Returns 1 on success or < 0 on error.
 218 */
 219static int get_relfile_envaddr(struct cmd_tbl *cmdtp, const char *file_path,
 220                               const char *envaddr_name)
 221{
 222        unsigned long file_addr;
 223        char *envaddr;
 224
 225        envaddr = from_env(envaddr_name);
 226
 227        if (!envaddr)
 228                return -ENOENT;
 229
 230        if (strict_strtoul(envaddr, 16, &file_addr) < 0)
 231                return -EINVAL;
 232
 233        return get_relfile(cmdtp, file_path, file_addr);
 234}
 235
 236/*
 237 * Allocates memory for and initializes a pxe_label. This uses malloc, so the
 238 * result must be free()'d to reclaim the memory.
 239 *
 240 * Returns NULL if malloc fails.
 241 */
 242static struct pxe_label *label_create(void)
 243{
 244        struct pxe_label *label;
 245
 246        label = malloc(sizeof(struct pxe_label));
 247
 248        if (!label)
 249                return NULL;
 250
 251        memset(label, 0, sizeof(struct pxe_label));
 252
 253        return label;
 254}
 255
 256/*
 257 * Free the memory used by a pxe_label, including that used by its name,
 258 * kernel, append and initrd members, if they're non NULL.
 259 *
 260 * So - be sure to only use dynamically allocated memory for the members of
 261 * the pxe_label struct, unless you want to clean it up first. These are
 262 * currently only created by the pxe file parsing code.
 263 */
 264static void label_destroy(struct pxe_label *label)
 265{
 266        if (label->name)
 267                free(label->name);
 268
 269        if (label->kernel)
 270                free(label->kernel);
 271
 272        if (label->config)
 273                free(label->config);
 274
 275        if (label->append)
 276                free(label->append);
 277
 278        if (label->initrd)
 279                free(label->initrd);
 280
 281        if (label->fdt)
 282                free(label->fdt);
 283
 284        if (label->fdtdir)
 285                free(label->fdtdir);
 286
 287        free(label);
 288}
 289
 290/*
 291 * Print a label and its string members if they're defined.
 292 *
 293 * This is passed as a callback to the menu code for displaying each
 294 * menu entry.
 295 */
 296static void label_print(void *data)
 297{
 298        struct pxe_label *label = data;
 299        const char *c = label->menu ? label->menu : label->name;
 300
 301        printf("%s:\t%s\n", label->num, c);
 302}
 303
 304/*
 305 * Boot a label that specified 'localboot'. This requires that the 'localcmd'
 306 * environment variable is defined. Its contents will be executed as U-Boot
 307 * command.  If the label specified an 'append' line, its contents will be
 308 * used to overwrite the contents of the 'bootargs' environment variable prior
 309 * to running 'localcmd'.
 310 *
 311 * Returns 1 on success or < 0 on error.
 312 */
 313static int label_localboot(struct pxe_label *label)
 314{
 315        char *localcmd;
 316
 317        localcmd = from_env("localcmd");
 318
 319        if (!localcmd)
 320                return -ENOENT;
 321
 322        if (label->append) {
 323                char bootargs[CONFIG_SYS_CBSIZE];
 324
 325                cli_simple_process_macros(label->append, bootargs);
 326                env_set("bootargs", bootargs);
 327        }
 328
 329        debug("running: %s\n", localcmd);
 330
 331        return run_command_list(localcmd, strlen(localcmd), 0);
 332}
 333
 334/*
 335 * Boot according to the contents of a pxe_label.
 336 *
 337 * If we can't boot for any reason, we return.  A successful boot never
 338 * returns.
 339 *
 340 * The kernel will be stored in the location given by the 'kernel_addr_r'
 341 * environment variable.
 342 *
 343 * If the label specifies an initrd file, it will be stored in the location
 344 * given by the 'ramdisk_addr_r' environment variable.
 345 *
 346 * If the label specifies an 'append' line, its contents will overwrite that
 347 * of the 'bootargs' environment variable.
 348 */
 349static int label_boot(struct cmd_tbl *cmdtp, struct pxe_label *label)
 350{
 351        char *bootm_argv[] = { "bootm", NULL, NULL, NULL, NULL };
 352        char initrd_str[28];
 353        char mac_str[29] = "";
 354        char ip_str[68] = "";
 355        char *fit_addr = NULL;
 356        int bootm_argc = 2;
 357        int len = 0;
 358        ulong kernel_addr;
 359        void *buf;
 360
 361        label_print(label);
 362
 363        label->attempted = 1;
 364
 365        if (label->localboot) {
 366                if (label->localboot_val >= 0)
 367                        label_localboot(label);
 368                return 0;
 369        }
 370
 371        if (!label->kernel) {
 372                printf("No kernel given, skipping %s\n",
 373                       label->name);
 374                return 1;
 375        }
 376
 377        if (label->initrd) {
 378                if (get_relfile_envaddr(cmdtp, label->initrd, "ramdisk_addr_r") < 0) {
 379                        printf("Skipping %s for failure retrieving initrd\n",
 380                               label->name);
 381                        return 1;
 382                }
 383
 384                bootm_argv[2] = initrd_str;
 385                strncpy(bootm_argv[2], env_get("ramdisk_addr_r"), 18);
 386                strcat(bootm_argv[2], ":");
 387                strncat(bootm_argv[2], env_get("filesize"), 9);
 388                bootm_argc = 3;
 389        }
 390
 391        if (get_relfile_envaddr(cmdtp, label->kernel, "kernel_addr_r") < 0) {
 392                printf("Skipping %s for failure retrieving kernel\n",
 393                       label->name);
 394                return 1;
 395        }
 396
 397        if (label->ipappend & 0x1) {
 398                sprintf(ip_str, " ip=%s:%s:%s:%s",
 399                        env_get("ipaddr"), env_get("serverip"),
 400                        env_get("gatewayip"), env_get("netmask"));
 401        }
 402
 403#ifdef CONFIG_CMD_NET
 404        if (label->ipappend & 0x2) {
 405                int err;
 406
 407                strcpy(mac_str, " BOOTIF=");
 408                err = format_mac_pxe(mac_str + 8, sizeof(mac_str) - 8);
 409                if (err < 0)
 410                        mac_str[0] = '\0';
 411        }
 412#endif
 413
 414        if ((label->ipappend & 0x3) || label->append) {
 415                char bootargs[CONFIG_SYS_CBSIZE] = "";
 416                char finalbootargs[CONFIG_SYS_CBSIZE];
 417
 418                if (strlen(label->append ?: "") +
 419                    strlen(ip_str) + strlen(mac_str) + 1 > sizeof(bootargs)) {
 420                        printf("bootarg overflow %zd+%zd+%zd+1 > %zd\n",
 421                               strlen(label->append ?: ""),
 422                               strlen(ip_str), strlen(mac_str),
 423                               sizeof(bootargs));
 424                        return 1;
 425                }
 426
 427                if (label->append)
 428                        strncpy(bootargs, label->append, sizeof(bootargs));
 429
 430                strcat(bootargs, ip_str);
 431                strcat(bootargs, mac_str);
 432
 433                cli_simple_process_macros(bootargs, finalbootargs);
 434                env_set("bootargs", finalbootargs);
 435                printf("append: %s\n", finalbootargs);
 436        }
 437
 438        bootm_argv[1] = env_get("kernel_addr_r");
 439        /* for FIT, append the configuration identifier */
 440        if (label->config) {
 441                int len = strlen(bootm_argv[1]) + strlen(label->config) + 1;
 442
 443                fit_addr = malloc(len);
 444                if (!fit_addr) {
 445                        printf("malloc fail (FIT address)\n");
 446                        return 1;
 447                }
 448                snprintf(fit_addr, len, "%s%s", bootm_argv[1], label->config);
 449                bootm_argv[1] = fit_addr;
 450        }
 451
 452        /*
 453         * fdt usage is optional:
 454         * It handles the following scenarios. All scenarios are exclusive
 455         *
 456         * Scenario 1: If fdt_addr_r specified and "fdt" label is defined in
 457         * pxe file, retrieve fdt blob from server. Pass fdt_addr_r to bootm,
 458         * and adjust argc appropriately.
 459         *
 460         * Scenario 2: If there is an fdt_addr specified, pass it along to
 461         * bootm, and adjust argc appropriately.
 462         *
 463         * Scenario 3: fdt blob is not available.
 464         */
 465        bootm_argv[3] = env_get("fdt_addr_r");
 466
 467        /* if fdt label is defined then get fdt from server */
 468        if (bootm_argv[3]) {
 469                char *fdtfile = NULL;
 470                char *fdtfilefree = NULL;
 471
 472                if (label->fdt) {
 473                        fdtfile = label->fdt;
 474                } else if (label->fdtdir) {
 475                        char *f1, *f2, *f3, *f4, *slash;
 476
 477                        f1 = env_get("fdtfile");
 478                        if (f1) {
 479                                f2 = "";
 480                                f3 = "";
 481                                f4 = "";
 482                        } else {
 483                                /*
 484                                 * For complex cases where this code doesn't
 485                                 * generate the correct filename, the board
 486                                 * code should set $fdtfile during early boot,
 487                                 * or the boot scripts should set $fdtfile
 488                                 * before invoking "pxe" or "sysboot".
 489                                 */
 490                                f1 = env_get("soc");
 491                                f2 = "-";
 492                                f3 = env_get("board");
 493                                f4 = ".dtb";
 494                        }
 495
 496                        len = strlen(label->fdtdir);
 497                        if (!len)
 498                                slash = "./";
 499                        else if (label->fdtdir[len - 1] != '/')
 500                                slash = "/";
 501                        else
 502                                slash = "";
 503
 504                        len = strlen(label->fdtdir) + strlen(slash) +
 505                                strlen(f1) + strlen(f2) + strlen(f3) +
 506                                strlen(f4) + 1;
 507                        fdtfilefree = malloc(len);
 508                        if (!fdtfilefree) {
 509                                printf("malloc fail (FDT filename)\n");
 510                                goto cleanup;
 511                        }
 512
 513                        snprintf(fdtfilefree, len, "%s%s%s%s%s%s",
 514                                 label->fdtdir, slash, f1, f2, f3, f4);
 515                        fdtfile = fdtfilefree;
 516                }
 517
 518                if (fdtfile) {
 519                        int err = get_relfile_envaddr(cmdtp, fdtfile,
 520                                                      "fdt_addr_r");
 521
 522                        free(fdtfilefree);
 523                        if (err < 0) {
 524                                printf("Skipping %s for failure retrieving fdt\n",
 525                                       label->name);
 526                                goto cleanup;
 527                        }
 528                } else {
 529                        bootm_argv[3] = NULL;
 530                }
 531        }
 532
 533        if (!bootm_argv[3])
 534                bootm_argv[3] = env_get("fdt_addr");
 535
 536        if (bootm_argv[3]) {
 537                if (!bootm_argv[2])
 538                        bootm_argv[2] = "-";
 539                bootm_argc = 4;
 540        }
 541
 542        kernel_addr = genimg_get_kernel_addr(bootm_argv[1]);
 543        buf = map_sysmem(kernel_addr, 0);
 544        /* Try bootm for legacy and FIT format image */
 545        if (genimg_get_format(buf) != IMAGE_FORMAT_INVALID)
 546                do_bootm(cmdtp, 0, bootm_argc, bootm_argv);
 547#ifdef CONFIG_CMD_BOOTI
 548        /* Try booting an AArch64 Linux kernel image */
 549        else
 550                do_booti(cmdtp, 0, bootm_argc, bootm_argv);
 551#elif defined(CONFIG_CMD_BOOTZ)
 552        /* Try booting a Image */
 553        else
 554                do_bootz(cmdtp, 0, bootm_argc, bootm_argv);
 555#endif
 556        unmap_sysmem(buf);
 557
 558cleanup:
 559        if (fit_addr)
 560                free(fit_addr);
 561        return 1;
 562}
 563
 564/*
 565 * Tokens for the pxe file parser.
 566 */
 567enum token_type {
 568        T_EOL,
 569        T_STRING,
 570        T_EOF,
 571        T_MENU,
 572        T_TITLE,
 573        T_TIMEOUT,
 574        T_LABEL,
 575        T_KERNEL,
 576        T_LINUX,
 577        T_APPEND,
 578        T_INITRD,
 579        T_LOCALBOOT,
 580        T_DEFAULT,
 581        T_PROMPT,
 582        T_INCLUDE,
 583        T_FDT,
 584        T_FDTDIR,
 585        T_ONTIMEOUT,
 586        T_IPAPPEND,
 587        T_BACKGROUND,
 588        T_INVALID
 589};
 590
 591/*
 592 * A token - given by a value and a type.
 593 */
 594struct token {
 595        char *val;
 596        enum token_type type;
 597};
 598
 599/*
 600 * Keywords recognized.
 601 */
 602static const struct token keywords[] = {
 603        {"menu", T_MENU},
 604        {"title", T_TITLE},
 605        {"timeout", T_TIMEOUT},
 606        {"default", T_DEFAULT},
 607        {"prompt", T_PROMPT},
 608        {"label", T_LABEL},
 609        {"kernel", T_KERNEL},
 610        {"linux", T_LINUX},
 611        {"localboot", T_LOCALBOOT},
 612        {"append", T_APPEND},
 613        {"initrd", T_INITRD},
 614        {"include", T_INCLUDE},
 615        {"devicetree", T_FDT},
 616        {"fdt", T_FDT},
 617        {"devicetreedir", T_FDTDIR},
 618        {"fdtdir", T_FDTDIR},
 619        {"ontimeout", T_ONTIMEOUT,},
 620        {"ipappend", T_IPAPPEND,},
 621        {"background", T_BACKGROUND,},
 622        {NULL, T_INVALID}
 623};
 624
 625/*
 626 * Since pxe(linux) files don't have a token to identify the start of a
 627 * literal, we have to keep track of when we're in a state where a literal is
 628 * expected vs when we're in a state a keyword is expected.
 629 */
 630enum lex_state {
 631        L_NORMAL = 0,
 632        L_KEYWORD,
 633        L_SLITERAL
 634};
 635
 636/*
 637 * get_string retrieves a string from *p and stores it as a token in
 638 * *t.
 639 *
 640 * get_string used for scanning both string literals and keywords.
 641 *
 642 * Characters from *p are copied into t-val until a character equal to
 643 * delim is found, or a NUL byte is reached. If delim has the special value of
 644 * ' ', any whitespace character will be used as a delimiter.
 645 *
 646 * If lower is unequal to 0, uppercase characters will be converted to
 647 * lowercase in the result. This is useful to make keywords case
 648 * insensitive.
 649 *
 650 * The location of *p is updated to point to the first character after the end
 651 * of the token - the ending delimiter.
 652 *
 653 * On success, the new value of t->val is returned. Memory for t->val is
 654 * allocated using malloc and must be free()'d to reclaim it.  If insufficient
 655 * memory is available, NULL is returned.
 656 */
 657static char *get_string(char **p, struct token *t, char delim, int lower)
 658{
 659        char *b, *e;
 660        size_t len, i;
 661
 662        /*
 663         * b and e both start at the beginning of the input stream.
 664         *
 665         * e is incremented until we find the ending delimiter, or a NUL byte
 666         * is reached. Then, we take e - b to find the length of the token.
 667         */
 668        b = *p;
 669        e = *p;
 670
 671        while (*e) {
 672                if ((delim == ' ' && isspace(*e)) || delim == *e)
 673                        break;
 674                e++;
 675        }
 676
 677        len = e - b;
 678
 679        /*
 680         * Allocate memory to hold the string, and copy it in, converting
 681         * characters to lowercase if lower is != 0.
 682         */
 683        t->val = malloc(len + 1);
 684        if (!t->val)
 685                return NULL;
 686
 687        for (i = 0; i < len; i++, b++) {
 688                if (lower)
 689                        t->val[i] = tolower(*b);
 690                else
 691                        t->val[i] = *b;
 692        }
 693
 694        t->val[len] = '\0';
 695
 696        /*
 697         * Update *p so the caller knows where to continue scanning.
 698         */
 699        *p = e;
 700
 701        t->type = T_STRING;
 702
 703        return t->val;
 704}
 705
 706/*
 707 * Populate a keyword token with a type and value.
 708 */
 709static void get_keyword(struct token *t)
 710{
 711        int i;
 712
 713        for (i = 0; keywords[i].val; i++) {
 714                if (!strcmp(t->val, keywords[i].val)) {
 715                        t->type = keywords[i].type;
 716                        break;
 717                }
 718        }
 719}
 720
 721/*
 722 * Get the next token.  We have to keep track of which state we're in to know
 723 * if we're looking to get a string literal or a keyword.
 724 *
 725 * *p is updated to point at the first character after the current token.
 726 */
 727static void get_token(char **p, struct token *t, enum lex_state state)
 728{
 729        char *c = *p;
 730
 731        t->type = T_INVALID;
 732
 733        /* eat non EOL whitespace */
 734        while (isblank(*c))
 735                c++;
 736
 737        /*
 738         * eat comments. note that string literals can't begin with #, but
 739         * can contain a # after their first character.
 740         */
 741        if (*c == '#') {
 742                while (*c && *c != '\n')
 743                        c++;
 744        }
 745
 746        if (*c == '\n') {
 747                t->type = T_EOL;
 748                c++;
 749        } else if (*c == '\0') {
 750                t->type = T_EOF;
 751                c++;
 752        } else if (state == L_SLITERAL) {
 753                get_string(&c, t, '\n', 0);
 754        } else if (state == L_KEYWORD) {
 755                /*
 756                 * when we expect a keyword, we first get the next string
 757                 * token delimited by whitespace, and then check if it
 758                 * matches a keyword in our keyword list. if it does, it's
 759                 * converted to a keyword token of the appropriate type, and
 760                 * if not, it remains a string token.
 761                 */
 762                get_string(&c, t, ' ', 1);
 763                get_keyword(t);
 764        }
 765
 766        *p = c;
 767}
 768
 769/*
 770 * Increment *c until we get to the end of the current line, or EOF.
 771 */
 772static void eol_or_eof(char **c)
 773{
 774        while (**c && **c != '\n')
 775                (*c)++;
 776}
 777
 778/*
 779 * All of these parse_* functions share some common behavior.
 780 *
 781 * They finish with *c pointing after the token they parse, and return 1 on
 782 * success, or < 0 on error.
 783 */
 784
 785/*
 786 * Parse a string literal and store a pointer it at *dst. String literals
 787 * terminate at the end of the line.
 788 */
 789static int parse_sliteral(char **c, char **dst)
 790{
 791        struct token t;
 792        char *s = *c;
 793
 794        get_token(c, &t, L_SLITERAL);
 795
 796        if (t.type != T_STRING) {
 797                printf("Expected string literal: %.*s\n", (int)(*c - s), s);
 798                return -EINVAL;
 799        }
 800
 801        *dst = t.val;
 802
 803        return 1;
 804}
 805
 806/*
 807 * Parse a base 10 (unsigned) integer and store it at *dst.
 808 */
 809static int parse_integer(char **c, int *dst)
 810{
 811        struct token t;
 812        char *s = *c;
 813
 814        get_token(c, &t, L_SLITERAL);
 815
 816        if (t.type != T_STRING) {
 817                printf("Expected string: %.*s\n", (int)(*c - s), s);
 818                return -EINVAL;
 819        }
 820
 821        *dst = simple_strtol(t.val, NULL, 10);
 822
 823        free(t.val);
 824
 825        return 1;
 826}
 827
 828static int parse_pxefile_top(struct cmd_tbl *cmdtp, char *p, unsigned long base,
 829                             struct pxe_menu *cfg, int nest_level);
 830
 831/*
 832 * Parse an include statement, and retrieve and parse the file it mentions.
 833 *
 834 * base should point to a location where it's safe to store the file, and
 835 * nest_level should indicate how many nested includes have occurred. For this
 836 * include, nest_level has already been incremented and doesn't need to be
 837 * incremented here.
 838 */
 839static int handle_include(struct cmd_tbl *cmdtp, char **c, unsigned long base,
 840                          struct pxe_menu *cfg, int nest_level)
 841{
 842        char *include_path;
 843        char *s = *c;
 844        int err;
 845        char *buf;
 846        int ret;
 847
 848        err = parse_sliteral(c, &include_path);
 849
 850        if (err < 0) {
 851                printf("Expected include path: %.*s\n", (int)(*c - s), s);
 852                return err;
 853        }
 854
 855        err = get_pxe_file(cmdtp, include_path, base);
 856
 857        if (err < 0) {
 858                printf("Couldn't retrieve %s\n", include_path);
 859                return err;
 860        }
 861
 862        buf = map_sysmem(base, 0);
 863        ret = parse_pxefile_top(cmdtp, buf, base, cfg, nest_level);
 864        unmap_sysmem(buf);
 865
 866        return ret;
 867}
 868
 869/*
 870 * Parse lines that begin with 'menu'.
 871 *
 872 * base and nest are provided to handle the 'menu include' case.
 873 *
 874 * base should point to a location where it's safe to store the included file.
 875 *
 876 * nest_level should be 1 when parsing the top level pxe file, 2 when parsing
 877 * a file it includes, 3 when parsing a file included by that file, and so on.
 878 */
 879static int parse_menu(struct cmd_tbl *cmdtp, char **c, struct pxe_menu *cfg,
 880                      unsigned long base, int nest_level)
 881{
 882        struct token t;
 883        char *s = *c;
 884        int err = 0;
 885
 886        get_token(c, &t, L_KEYWORD);
 887
 888        switch (t.type) {
 889        case T_TITLE:
 890                err = parse_sliteral(c, &cfg->title);
 891
 892                break;
 893
 894        case T_INCLUDE:
 895                err = handle_include(cmdtp, c, base, cfg, nest_level + 1);
 896                break;
 897
 898        case T_BACKGROUND:
 899                err = parse_sliteral(c, &cfg->bmp);
 900                break;
 901
 902        default:
 903                printf("Ignoring malformed menu command: %.*s\n",
 904                       (int)(*c - s), s);
 905        }
 906
 907        if (err < 0)
 908                return err;
 909
 910        eol_or_eof(c);
 911
 912        return 1;
 913}
 914
 915/*
 916 * Handles parsing a 'menu line' when we're parsing a label.
 917 */
 918static int parse_label_menu(char **c, struct pxe_menu *cfg,
 919                            struct pxe_label *label)
 920{
 921        struct token t;
 922        char *s;
 923
 924        s = *c;
 925
 926        get_token(c, &t, L_KEYWORD);
 927
 928        switch (t.type) {
 929        case T_DEFAULT:
 930                if (!cfg->default_label)
 931                        cfg->default_label = strdup(label->name);
 932
 933                if (!cfg->default_label)
 934                        return -ENOMEM;
 935
 936                break;
 937        case T_LABEL:
 938                parse_sliteral(c, &label->menu);
 939                break;
 940        default:
 941                printf("Ignoring malformed menu command: %.*s\n",
 942                       (int)(*c - s), s);
 943        }
 944
 945        eol_or_eof(c);
 946
 947        return 0;
 948}
 949
 950/*
 951 * Handles parsing a 'kernel' label.
 952 * expecting "filename" or "<fit_filename>#cfg"
 953 */
 954static int parse_label_kernel(char **c, struct pxe_label *label)
 955{
 956        char *s;
 957        int err;
 958
 959        err = parse_sliteral(c, &label->kernel);
 960        if (err < 0)
 961                return err;
 962
 963        s = strstr(label->kernel, "#");
 964        if (!s)
 965                return 1;
 966
 967        label->config = malloc(strlen(s) + 1);
 968        if (!label->config)
 969                return -ENOMEM;
 970
 971        strcpy(label->config, s);
 972        *s = 0;
 973
 974        return 1;
 975}
 976
 977/*
 978 * Parses a label and adds it to the list of labels for a menu.
 979 *
 980 * A label ends when we either get to the end of a file, or
 981 * get some input we otherwise don't have a handler defined
 982 * for.
 983 *
 984 */
 985static int parse_label(char **c, struct pxe_menu *cfg)
 986{
 987        struct token t;
 988        int len;
 989        char *s = *c;
 990        struct pxe_label *label;
 991        int err;
 992
 993        label = label_create();
 994        if (!label)
 995                return -ENOMEM;
 996
 997        err = parse_sliteral(c, &label->name);
 998        if (err < 0) {
 999                printf("Expected label name: %.*s\n", (int)(*c - s), s);
1000                label_destroy(label);
1001                return -EINVAL;
1002        }
1003
1004        list_add_tail(&label->list, &cfg->labels);
1005
1006        while (1) {
1007                s = *c;
1008                get_token(c, &t, L_KEYWORD);
1009
1010                err = 0;
1011                switch (t.type) {
1012                case T_MENU:
1013                        err = parse_label_menu(c, cfg, label);
1014                        break;
1015
1016                case T_KERNEL:
1017                case T_LINUX:
1018                        err = parse_label_kernel(c, label);
1019                        break;
1020
1021                case T_APPEND:
1022                        err = parse_sliteral(c, &label->append);
1023                        if (label->initrd)
1024                                break;
1025                        s = strstr(label->append, "initrd=");
1026                        if (!s)
1027                                break;
1028                        s += 7;
1029                        len = (int)(strchr(s, ' ') - s);
1030                        label->initrd = malloc(len + 1);
1031                        strncpy(label->initrd, s, len);
1032                        label->initrd[len] = '\0';
1033
1034                        break;
1035
1036                case T_INITRD:
1037                        if (!label->initrd)
1038                                err = parse_sliteral(c, &label->initrd);
1039                        break;
1040
1041                case T_FDT:
1042                        if (!label->fdt)
1043                                err = parse_sliteral(c, &label->fdt);
1044                        break;
1045
1046                case T_FDTDIR:
1047                        if (!label->fdtdir)
1048                                err = parse_sliteral(c, &label->fdtdir);
1049                        break;
1050
1051                case T_LOCALBOOT:
1052                        label->localboot = 1;
1053                        err = parse_integer(c, &label->localboot_val);
1054                        break;
1055
1056                case T_IPAPPEND:
1057                        err = parse_integer(c, &label->ipappend);
1058                        break;
1059
1060                case T_EOL:
1061                        break;
1062
1063                default:
1064                        /*
1065                         * put the token back! we don't want it - it's the end
1066                         * of a label and whatever token this is, it's
1067                         * something for the menu level context to handle.
1068                         */
1069                        *c = s;
1070                        return 1;
1071                }
1072
1073                if (err < 0)
1074                        return err;
1075        }
1076}
1077
1078/*
1079 * This 16 comes from the limit pxelinux imposes on nested includes.
1080 *
1081 * There is no reason at all we couldn't do more, but some limit helps prevent
1082 * infinite (until crash occurs) recursion if a file tries to include itself.
1083 */
1084#define MAX_NEST_LEVEL 16
1085
1086/*
1087 * Entry point for parsing a menu file. nest_level indicates how many times
1088 * we've nested in includes.  It will be 1 for the top level menu file.
1089 *
1090 * Returns 1 on success, < 0 on error.
1091 */
1092static int parse_pxefile_top(struct cmd_tbl *cmdtp, char *p, unsigned long base,
1093                             struct pxe_menu *cfg, int nest_level)
1094{
1095        struct token t;
1096        char *s, *b, *label_name;
1097        int err;
1098
1099        b = p;
1100
1101        if (nest_level > MAX_NEST_LEVEL) {
1102                printf("Maximum nesting (%d) exceeded\n", MAX_NEST_LEVEL);
1103                return -EMLINK;
1104        }
1105
1106        while (1) {
1107                s = p;
1108
1109                get_token(&p, &t, L_KEYWORD);
1110
1111                err = 0;
1112                switch (t.type) {
1113                case T_MENU:
1114                        cfg->prompt = 1;
1115                        err = parse_menu(cmdtp, &p, cfg,
1116                                         base + ALIGN(strlen(b) + 1, 4),
1117                                         nest_level);
1118                        break;
1119
1120                case T_TIMEOUT:
1121                        err = parse_integer(&p, &cfg->timeout);
1122                        break;
1123
1124                case T_LABEL:
1125                        err = parse_label(&p, cfg);
1126                        break;
1127
1128                case T_DEFAULT:
1129                case T_ONTIMEOUT:
1130                        err = parse_sliteral(&p, &label_name);
1131
1132                        if (label_name) {
1133                                if (cfg->default_label)
1134                                        free(cfg->default_label);
1135
1136                                cfg->default_label = label_name;
1137                        }
1138
1139                        break;
1140
1141                case T_INCLUDE:
1142                        err = handle_include(cmdtp, &p,
1143                                             base + ALIGN(strlen(b), 4), cfg,
1144                                             nest_level + 1);
1145                        break;
1146
1147                case T_PROMPT:
1148                        eol_or_eof(&p);
1149                        break;
1150
1151                case T_EOL:
1152                        break;
1153
1154                case T_EOF:
1155                        return 1;
1156
1157                default:
1158                        printf("Ignoring unknown command: %.*s\n",
1159                               (int)(p - s), s);
1160                        eol_or_eof(&p);
1161                }
1162
1163                if (err < 0)
1164                        return err;
1165        }
1166}
1167
1168/*
1169 * Free the memory used by a pxe_menu and its labels.
1170 */
1171void destroy_pxe_menu(struct pxe_menu *cfg)
1172{
1173        struct list_head *pos, *n;
1174        struct pxe_label *label;
1175
1176        if (cfg->title)
1177                free(cfg->title);
1178
1179        if (cfg->default_label)
1180                free(cfg->default_label);
1181
1182        list_for_each_safe(pos, n, &cfg->labels) {
1183                label = list_entry(pos, struct pxe_label, list);
1184
1185                label_destroy(label);
1186        }
1187
1188        free(cfg);
1189}
1190
1191/*
1192 * Entry point for parsing a pxe file. This is only used for the top level
1193 * file.
1194 *
1195 * Returns NULL if there is an error, otherwise, returns a pointer to a
1196 * pxe_menu struct populated with the results of parsing the pxe file (and any
1197 * files it includes). The resulting pxe_menu struct can be free()'d by using
1198 * the destroy_pxe_menu() function.
1199 */
1200struct pxe_menu *parse_pxefile(struct cmd_tbl *cmdtp, unsigned long menucfg)
1201{
1202        struct pxe_menu *cfg;
1203        char *buf;
1204        int r;
1205
1206        cfg = malloc(sizeof(struct pxe_menu));
1207
1208        if (!cfg)
1209                return NULL;
1210
1211        memset(cfg, 0, sizeof(struct pxe_menu));
1212
1213        INIT_LIST_HEAD(&cfg->labels);
1214
1215        buf = map_sysmem(menucfg, 0);
1216        r = parse_pxefile_top(cmdtp, buf, menucfg, cfg, 1);
1217        unmap_sysmem(buf);
1218
1219        if (r < 0) {
1220                destroy_pxe_menu(cfg);
1221                return NULL;
1222        }
1223
1224        return cfg;
1225}
1226
1227/*
1228 * Converts a pxe_menu struct into a menu struct for use with U-Boot's generic
1229 * menu code.
1230 */
1231static struct menu *pxe_menu_to_menu(struct pxe_menu *cfg)
1232{
1233        struct pxe_label *label;
1234        struct list_head *pos;
1235        struct menu *m;
1236        int err;
1237        int i = 1;
1238        char *default_num = NULL;
1239
1240        /*
1241         * Create a menu and add items for all the labels.
1242         */
1243        m = menu_create(cfg->title, DIV_ROUND_UP(cfg->timeout, 10),
1244                        cfg->prompt, NULL, label_print, NULL, NULL);
1245
1246        if (!m)
1247                return NULL;
1248
1249        list_for_each(pos, &cfg->labels) {
1250                label = list_entry(pos, struct pxe_label, list);
1251
1252                sprintf(label->num, "%d", i++);
1253                if (menu_item_add(m, label->num, label) != 1) {
1254                        menu_destroy(m);
1255                        return NULL;
1256                }
1257                if (cfg->default_label &&
1258                    (strcmp(label->name, cfg->default_label) == 0))
1259                        default_num = label->num;
1260        }
1261
1262        /*
1263         * After we've created items for each label in the menu, set the
1264         * menu's default label if one was specified.
1265         */
1266        if (default_num) {
1267                err = menu_default_set(m, default_num);
1268                if (err != 1) {
1269                        if (err != -ENOENT) {
1270                                menu_destroy(m);
1271                                return NULL;
1272                        }
1273
1274                        printf("Missing default: %s\n", cfg->default_label);
1275                }
1276        }
1277
1278        return m;
1279}
1280
1281/*
1282 * Try to boot any labels we have yet to attempt to boot.
1283 */
1284static void boot_unattempted_labels(struct cmd_tbl *cmdtp, struct pxe_menu *cfg)
1285{
1286        struct list_head *pos;
1287        struct pxe_label *label;
1288
1289        list_for_each(pos, &cfg->labels) {
1290                label = list_entry(pos, struct pxe_label, list);
1291
1292                if (!label->attempted)
1293                        label_boot(cmdtp, label);
1294        }
1295}
1296
1297/*
1298 * Boot the system as prescribed by a pxe_menu.
1299 *
1300 * Use the menu system to either get the user's choice or the default, based
1301 * on config or user input.  If there is no default or user's choice,
1302 * attempted to boot labels in the order they were given in pxe files.
1303 * If the default or user's choice fails to boot, attempt to boot other
1304 * labels in the order they were given in pxe files.
1305 *
1306 * If this function returns, there weren't any labels that successfully
1307 * booted, or the user interrupted the menu selection via ctrl+c.
1308 */
1309void handle_pxe_menu(struct cmd_tbl *cmdtp, struct pxe_menu *cfg)
1310{
1311        void *choice;
1312        struct menu *m;
1313        int err;
1314
1315#ifdef CONFIG_CMD_BMP
1316        /* display BMP if available */
1317        if (cfg->bmp) {
1318                if (get_relfile(cmdtp, cfg->bmp, image_load_addr)) {
1319                        if (CONFIG_IS_ENABLED(CMD_CLS))
1320                                run_command("cls", 0);
1321                        bmp_display(image_load_addr,
1322                                    BMP_ALIGN_CENTER, BMP_ALIGN_CENTER);
1323                } else {
1324                        printf("Skipping background bmp %s for failure\n",
1325                               cfg->bmp);
1326                }
1327        }
1328#endif
1329
1330        m = pxe_menu_to_menu(cfg);
1331        if (!m)
1332                return;
1333
1334        err = menu_get_choice(m, &choice);
1335
1336        menu_destroy(m);
1337
1338        /*
1339         * err == 1 means we got a choice back from menu_get_choice.
1340         *
1341         * err == -ENOENT if the menu was setup to select the default but no
1342         * default was set. in that case, we should continue trying to boot
1343         * labels that haven't been attempted yet.
1344         *
1345         * otherwise, the user interrupted or there was some other error and
1346         * we give up.
1347         */
1348
1349        if (err == 1) {
1350                err = label_boot(cmdtp, choice);
1351                if (!err)
1352                        return;
1353        } else if (err != -ENOENT) {
1354                return;
1355        }
1356
1357        boot_unattempted_labels(cmdtp, cfg);
1358}
1359