pxe: implement fdtdir extlinux.conf tag
authorStephen Warren <swarren@nvidia.com>
Tue, 28 Jan 2014 21:50:10 +0000 (14:50 -0700)
committerTom Rini <trini@ti.com>
Tue, 4 Feb 2014 16:51:18 +0000 (11:51 -0500)
People who write (or scripts that auto-generate) extlinux.conf don't
want to know about HW-specific information such as FDT filenames. Create
a new extlinux.conf tag "fdtdir" that specifies only the directory where
FDT files are located, and defer all knowledge of the filename to U-Boot.
The algorithm implemented is:

==========
if $fdt_addr_r is set:
  if "fdt" tag was specified in extlinux.conf:
    load the FDT from the filename in the tag
  else if "fdtdir" tag was specified in extlinux.conf:
    if "fdtfile" is set in the environment:
      load the FDT from filename in "$fdtfile"
    else:
      load the FDT from some automatically generated filename

if no FDT file was loaded, and $fdtaddr is set:
  # This indicates an FDT packaged with firmware
  use the FDT at $fdtaddr
==========

A small part of an example /boot/extlinux.conf might be:

==========
LABEL primary
        LINUX zImage
        FDTDIR ./

LABEL failsafe
        LINUX bkp/zImage
        FDTDIR bkp/
==========

... with /boot/tegra20-seaboard.dtb or /boot/bkp/tegra20-seaboard.dtb
being loaded by the sysboot/pxe code.

Signed-off-by: Stephen Warren <swarren@nvidia.com>
common/cmd_pxe.c

index 4f00b1a607d4bf8dc836f8db577ebae53c67c123..2bd572d64691db5446d17d7af683405c9ceada9e 100644 (file)
@@ -445,6 +445,7 @@ struct pxe_label {
        char *append;
        char *initrd;
        char *fdt;
+       char *fdtdir;
        int ipappend;
        int attempted;
        int localboot;
@@ -517,6 +518,9 @@ static void label_destroy(struct pxe_label *label)
        if (label->fdt)
                free(label->fdt);
 
+       if (label->fdtdir)
+               free(label->fdtdir);
+
        free(label);
 }
 
@@ -675,13 +679,67 @@ static int label_boot(cmd_tbl_t *cmdtp, struct pxe_label *label)
        bootm_argv[3] = getenv("fdt_addr_r");
 
        /* if fdt label is defined then get fdt from server */
-       if (bootm_argv[3] && label->fdt) {
-               if (get_relfile_envaddr(cmdtp, label->fdt, "fdt_addr_r") < 0) {
-                       printf("Skipping %s for failure retrieving fdt\n",
-                                       label->name);
-                       return 1;
+       if (bootm_argv[3]) {
+               char *fdtfile = NULL;
+               char *fdtfilefree = NULL;
+
+               if (label->fdt) {
+                       fdtfile = label->fdt;
+               } else if (label->fdtdir) {
+                       fdtfile = getenv("fdtfile");
+                       /*
+                        * For complex cases, it might be worth calling a
+                        * board- or SoC-provided function here to provide a
+                        * better default:
+                        *
+                        * if (!fdtfile)
+                        *     fdtfile = gen_fdtfile();
+                        *
+                        * If this is added, be sure to keep the default below,
+                        * or move it to the default weak implementation of
+                        * gen_fdtfile().
+                        */
+                       if (!fdtfile) {
+                               char *soc = getenv("soc");
+                               char *board = getenv("board");
+                               char *slash;
+
+                               len = strlen(label->fdtdir);
+                               if (!len)
+                                       slash = "./";
+                               else if (label->fdtdir[len - 1] != '/')
+                                       slash = "/";
+                               else
+                                       slash = "";
+
+                               len = strlen(label->fdtdir) + strlen(slash) +
+                                       strlen(soc) + 1 + strlen(board) + 5;
+                               fdtfilefree = malloc(len);
+                               if (!fdtfilefree) {
+                                       printf("malloc fail (FDT filename)\n");
+                                       return 1;
+                               }
+
+                               snprintf(fdtfilefree, len, "%s%s%s-%s.dtb",
+                                       label->fdtdir, slash, soc, board);
+                               fdtfile = fdtfilefree;
+                       }
                }
-       } else
+
+               if (fdtfile) {
+                       int err = get_relfile_envaddr(cmdtp, fdtfile, "fdt_addr_r");
+                       free(fdtfilefree);
+                       if (err < 0) {
+                               printf("Skipping %s for failure retrieving fdt\n",
+                                               label->name);
+                               return 1;
+                       }
+               } else {
+                       bootm_argv[3] = NULL;
+               }
+       }
+
+       if (!bootm_argv[3])
                bootm_argv[3] = getenv("fdt_addr");
 
        if (bootm_argv[3])
@@ -716,6 +774,7 @@ enum token_type {
        T_PROMPT,
        T_INCLUDE,
        T_FDT,
+       T_FDTDIR,
        T_ONTIMEOUT,
        T_IPAPPEND,
        T_INVALID
@@ -747,6 +806,8 @@ static const struct token keywords[] = {
        {"include", T_INCLUDE},
        {"devicetree", T_FDT},
        {"fdt", T_FDT},
+       {"devicetreedir", T_FDTDIR},
+       {"fdtdir", T_FDTDIR},
        {"ontimeout", T_ONTIMEOUT,},
        {"ipappend", T_IPAPPEND,},
        {NULL, T_INVALID}
@@ -1135,6 +1196,11 @@ static int parse_label(char **c, struct pxe_menu *cfg)
                                err = parse_sliteral(c, &label->fdt);
                        break;
 
+               case T_FDTDIR:
+                       if (!label->fdtdir)
+                               err = parse_sliteral(c, &label->fdtdir);
+                       break;
+
                case T_LOCALBOOT:
                        label->localboot = 1;
                        err = parse_integer(c, &label->localboot_val);