uboot/cmd/elf.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2001 William L. Pitts
   3 * All rights reserved.
   4 *
   5 * Redistribution and use in source and binary forms are freely
   6 * permitted provided that the above copyright notice and this
   7 * paragraph and the following disclaimer are duplicated in all
   8 * such forms.
   9 *
  10 * This software is provided "AS IS" and without any express or
  11 * implied warranties, including, without limitation, the implied
  12 * warranties of merchantability and fitness for a particular
  13 * purpose.
  14 */
  15
  16#include <common.h>
  17#include <command.h>
  18#include <elf.h>
  19#include <net.h>
  20#include <vxworks.h>
  21#ifdef CONFIG_X86
  22#include <asm/e820.h>
  23#include <linux/linkage.h>
  24#endif
  25
  26/*
  27 * A very simple elf loader, assumes the image is valid, returns the
  28 * entry point address.
  29 */
  30static unsigned long load_elf_image_phdr(unsigned long addr)
  31{
  32        Elf32_Ehdr *ehdr; /* Elf header structure pointer */
  33        Elf32_Phdr *phdr; /* Program header structure pointer */
  34        int i;
  35
  36        ehdr = (Elf32_Ehdr *)addr;
  37        phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff);
  38
  39        /* Load each program header */
  40        for (i = 0; i < ehdr->e_phnum; ++i) {
  41                void *dst = (void *)(uintptr_t)phdr->p_paddr;
  42                void *src = (void *)addr + phdr->p_offset;
  43                debug("Loading phdr %i to 0x%p (%i bytes)\n",
  44                      i, dst, phdr->p_filesz);
  45                if (phdr->p_filesz)
  46                        memcpy(dst, src, phdr->p_filesz);
  47                if (phdr->p_filesz != phdr->p_memsz)
  48                        memset(dst + phdr->p_filesz, 0x00,
  49                               phdr->p_memsz - phdr->p_filesz);
  50                flush_cache((unsigned long)dst, phdr->p_filesz);
  51                ++phdr;
  52        }
  53
  54        return ehdr->e_entry;
  55}
  56
  57static unsigned long load_elf_image_shdr(unsigned long addr)
  58{
  59        Elf32_Ehdr *ehdr; /* Elf header structure pointer */
  60        Elf32_Shdr *shdr; /* Section header structure pointer */
  61        unsigned char *strtab = 0; /* String table pointer */
  62        unsigned char *image; /* Binary image pointer */
  63        int i; /* Loop counter */
  64
  65        ehdr = (Elf32_Ehdr *)addr;
  66
  67        /* Find the section header string table for output info */
  68        shdr = (Elf32_Shdr *)(addr + ehdr->e_shoff +
  69                             (ehdr->e_shstrndx * sizeof(Elf32_Shdr)));
  70
  71        if (shdr->sh_type == SHT_STRTAB)
  72                strtab = (unsigned char *)(addr + shdr->sh_offset);
  73
  74        /* Load each appropriate section */
  75        for (i = 0; i < ehdr->e_shnum; ++i) {
  76                shdr = (Elf32_Shdr *)(addr + ehdr->e_shoff +
  77                                     (i * sizeof(Elf32_Shdr)));
  78
  79                if (!(shdr->sh_flags & SHF_ALLOC) ||
  80                    shdr->sh_addr == 0 || shdr->sh_size == 0) {
  81                        continue;
  82                }
  83
  84                if (strtab) {
  85                        debug("%sing %s @ 0x%08lx (%ld bytes)\n",
  86                              (shdr->sh_type == SHT_NOBITS) ? "Clear" : "Load",
  87                               &strtab[shdr->sh_name],
  88                               (unsigned long)shdr->sh_addr,
  89                               (long)shdr->sh_size);
  90                }
  91
  92                if (shdr->sh_type == SHT_NOBITS) {
  93                        memset((void *)(uintptr_t)shdr->sh_addr, 0,
  94                               shdr->sh_size);
  95                } else {
  96                        image = (unsigned char *)addr + shdr->sh_offset;
  97                        memcpy((void *)(uintptr_t)shdr->sh_addr,
  98                               (const void *)image, shdr->sh_size);
  99                }
 100                flush_cache(shdr->sh_addr, shdr->sh_size);
 101        }
 102
 103        return ehdr->e_entry;
 104}
 105
 106/* Allow ports to override the default behavior */
 107static unsigned long do_bootelf_exec(ulong (*entry)(int, char * const[]),
 108                                     int argc, char * const argv[])
 109{
 110        unsigned long ret;
 111
 112        /*
 113         * QNX images require the data cache is disabled.
 114         * Data cache is already flushed, so just turn it off.
 115         */
 116        int dcache = dcache_status();
 117        if (dcache)
 118                dcache_disable();
 119
 120        /*
 121         * pass address parameter as argv[0] (aka command name),
 122         * and all remaining args
 123         */
 124        ret = entry(argc, argv);
 125
 126        if (dcache)
 127                dcache_enable();
 128
 129        return ret;
 130}
 131
 132/*
 133 * Determine if a valid ELF image exists at the given memory location.
 134 * First look at the ELF header magic field, then make sure that it is
 135 * executable.
 136 */
 137int valid_elf_image(unsigned long addr)
 138{
 139        Elf32_Ehdr *ehdr; /* Elf header structure pointer */
 140
 141        ehdr = (Elf32_Ehdr *)addr;
 142
 143        if (!IS_ELF(*ehdr)) {
 144                printf("## No elf image at address 0x%08lx\n", addr);
 145                return 0;
 146        }
 147
 148        if (ehdr->e_type != ET_EXEC) {
 149                printf("## Not a 32-bit elf image at address 0x%08lx\n", addr);
 150                return 0;
 151        }
 152
 153        return 1;
 154}
 155
 156/* Interpreter command to boot an arbitrary ELF image from memory */
 157int do_bootelf(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 158{
 159        unsigned long addr; /* Address of the ELF image */
 160        unsigned long rc; /* Return value from user code */
 161        char *sload, *saddr;
 162        const char *ep = getenv("autostart");
 163
 164        int rcode = 0;
 165
 166        sload = saddr = NULL;
 167        if (argc == 3) {
 168                sload = argv[1];
 169                saddr = argv[2];
 170        } else if (argc == 2) {
 171                if (argv[1][0] == '-')
 172                        sload = argv[1];
 173                else
 174                        saddr = argv[1];
 175        }
 176
 177        if (saddr)
 178                addr = simple_strtoul(saddr, NULL, 16);
 179        else
 180                addr = load_addr;
 181
 182        if (!valid_elf_image(addr))
 183                return 1;
 184
 185        if (sload && sload[1] == 'p')
 186                addr = load_elf_image_phdr(addr);
 187        else
 188                addr = load_elf_image_shdr(addr);
 189
 190        if (ep && !strcmp(ep, "no"))
 191                return rcode;
 192
 193        printf("## Starting application at 0x%08lx ...\n", addr);
 194
 195        /*
 196         * pass address parameter as argv[0] (aka command name),
 197         * and all remaining args
 198         */
 199        rc = do_bootelf_exec((void *)addr, argc - 1, argv + 1);
 200        if (rc != 0)
 201                rcode = 1;
 202
 203        printf("## Application terminated, rc = 0x%lx\n", rc);
 204
 205        return rcode;
 206}
 207
 208/*
 209 * Interpreter command to boot VxWorks from a memory image.  The image can
 210 * be either an ELF image or a raw binary.  Will attempt to setup the
 211 * bootline and other parameters correctly.
 212 */
 213int do_bootvx(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 214{
 215        unsigned long addr; /* Address of image */
 216        unsigned long bootaddr; /* Address to put the bootline */
 217        char *bootline; /* Text of the bootline */
 218        char *tmp; /* Temporary char pointer */
 219        char build_buf[128]; /* Buffer for building the bootline */
 220        int ptr = 0;
 221#ifdef CONFIG_X86
 222        struct e820info *info;
 223        struct e820entry *data;
 224#endif
 225
 226        /*
 227         * Check the loadaddr variable.
 228         * If we don't know where the image is then we're done.
 229         */
 230        if (argc < 2)
 231                addr = load_addr;
 232        else
 233                addr = simple_strtoul(argv[1], NULL, 16);
 234
 235#if defined(CONFIG_CMD_NET)
 236        /*
 237         * Check to see if we need to tftp the image ourselves
 238         * before starting
 239         */
 240        if ((argc == 2) && (strcmp(argv[1], "tftp") == 0)) {
 241                if (net_loop(TFTPGET) <= 0)
 242                        return 1;
 243                printf("Automatic boot of VxWorks image at address 0x%08lx ...\n",
 244                        addr);
 245        }
 246#endif
 247
 248        /*
 249         * This should equate to
 250         * NV_RAM_ADRS + NV_BOOT_OFFSET + NV_ENET_OFFSET
 251         * from the VxWorks BSP header files.
 252         * This will vary from board to board
 253         */
 254#if defined(CONFIG_WALNUT)
 255        tmp = (char *)CONFIG_SYS_NVRAM_BASE_ADDR + 0x500;
 256        eth_getenv_enetaddr("ethaddr", (uchar *)build_buf);
 257        memcpy(tmp, &build_buf[3], 3);
 258#elif defined(CONFIG_SYS_VXWORKS_MAC_PTR)
 259        tmp = (char *)CONFIG_SYS_VXWORKS_MAC_PTR;
 260        eth_getenv_enetaddr("ethaddr", (uchar *)build_buf);
 261        memcpy(tmp, build_buf, 6);
 262#else
 263        puts("## Ethernet MAC address not copied to NV RAM\n");
 264#endif
 265
 266        /*
 267         * Use bootaddr to find the location in memory that VxWorks
 268         * will look for the bootline string. The default value is
 269         * (LOCAL_MEM_LOCAL_ADRS + BOOT_LINE_OFFSET) as defined by
 270         * VxWorks BSP. For example, on PowerPC it defaults to 0x4200.
 271         */
 272        tmp = getenv("bootaddr");
 273        if (!tmp) {
 274                printf("## VxWorks bootline address not specified\n");
 275        } else {
 276                bootaddr = simple_strtoul(tmp, NULL, 16);
 277
 278                /*
 279                 * Check to see if the bootline is defined in the 'bootargs'
 280                 * parameter. If it is not defined, we may be able to
 281                 * construct the info.
 282                 */
 283                bootline = getenv("bootargs");
 284                if (bootline) {
 285                        memcpy((void *)bootaddr, bootline,
 286                               max(strlen(bootline), (size_t)255));
 287                        flush_cache(bootaddr, max(strlen(bootline),
 288                                                  (size_t)255));
 289                } else {
 290                        tmp = getenv("bootdev");
 291                        if (tmp) {
 292                                strcpy(build_buf, tmp);
 293                                ptr = strlen(tmp);
 294                        } else
 295                                printf("## VxWorks boot device not specified\n");
 296
 297                        tmp = getenv("bootfile");
 298                        if (tmp)
 299                                ptr += sprintf(build_buf + ptr,
 300                                               "host:%s ", tmp);
 301                        else
 302                                ptr += sprintf(build_buf + ptr,
 303                                               "host:vxWorks ");
 304
 305                        /*
 306                         * The following parameters are only needed if 'bootdev'
 307                         * is an ethernet device, otherwise they are optional.
 308                         */
 309                        tmp = getenv("ipaddr");
 310                        if (tmp) {
 311                                ptr += sprintf(build_buf + ptr, "e=%s", tmp);
 312                                tmp = getenv("netmask");
 313                                if (tmp) {
 314                                        u32 mask = getenv_ip("netmask").s_addr;
 315                                        ptr += sprintf(build_buf + ptr,
 316                                                       ":%08x ", ntohl(mask));
 317                                } else {
 318                                        ptr += sprintf(build_buf + ptr, " ");
 319                                }
 320                        }
 321
 322                        tmp = getenv("serverip");
 323                        if (tmp)
 324                                ptr += sprintf(build_buf + ptr, "h=%s ", tmp);
 325
 326                        tmp = getenv("gatewayip");
 327                        if (tmp)
 328                                ptr += sprintf(build_buf + ptr, "g=%s ", tmp);
 329
 330                        tmp = getenv("hostname");
 331                        if (tmp)
 332                                ptr += sprintf(build_buf + ptr, "tn=%s ", tmp);
 333
 334                        tmp = getenv("othbootargs");
 335                        if (tmp) {
 336                                strcpy(build_buf + ptr, tmp);
 337                                ptr += strlen(tmp);
 338                        }
 339
 340                        memcpy((void *)bootaddr, build_buf,
 341                               max(strlen(build_buf), (size_t)255));
 342                        flush_cache(bootaddr, max(strlen(build_buf),
 343                                                  (size_t)255));
 344                }
 345
 346                printf("## Using bootline (@ 0x%lx): %s\n", bootaddr,
 347                       (char *)bootaddr);
 348        }
 349
 350#ifdef CONFIG_X86
 351        /*
 352         * Since E820 information is critical to the kernel, if we don't
 353         * specify these in the environments, use a default one.
 354         */
 355        tmp = getenv("e820data");
 356        if (tmp)
 357                data = (struct e820entry *)simple_strtoul(tmp, NULL, 16);
 358        else
 359                data = (struct e820entry *)VXWORKS_E820_DATA_ADDR;
 360        tmp = getenv("e820info");
 361        if (tmp)
 362                info = (struct e820info *)simple_strtoul(tmp, NULL, 16);
 363        else
 364                info = (struct e820info *)VXWORKS_E820_INFO_ADDR;
 365
 366        memset(info, 0, sizeof(struct e820info));
 367        info->sign = E820_SIGNATURE;
 368        info->entries = install_e820_map(E820MAX, data);
 369        info->addr = (info->entries - 1) * sizeof(struct e820entry) +
 370                     VXWORKS_E820_DATA_ADDR;
 371#endif
 372
 373        /*
 374         * If the data at the load address is an elf image, then
 375         * treat it like an elf image. Otherwise, assume that it is a
 376         * binary image.
 377         */
 378        if (valid_elf_image(addr))
 379                addr = load_elf_image_shdr(addr);
 380        else
 381                puts("## Not an ELF image, assuming binary\n");
 382
 383        printf("## Starting vxWorks at 0x%08lx ...\n", addr);
 384
 385        dcache_disable();
 386#ifdef CONFIG_X86
 387        /* VxWorks on x86 uses stack to pass parameters */
 388        ((asmlinkage void (*)(int))addr)(0);
 389#else
 390        ((void (*)(int))addr)(0);
 391#endif
 392
 393        puts("## vxWorks terminated\n");
 394
 395        return 1;
 396}
 397
 398U_BOOT_CMD(
 399        bootelf, 3, 0, do_bootelf,
 400        "Boot from an ELF image in memory",
 401        "[-p|-s] [address]\n"
 402        "\t- load ELF image at [address] via program headers (-p)\n"
 403        "\t  or via section headers (-s)"
 404);
 405
 406U_BOOT_CMD(
 407        bootvx, 2, 0, do_bootvx,
 408        "Boot vxWorks from an ELF image",
 409        " [address] - load address of vxWorks ELF image."
 410);
 411