cmd: elf: Reorder load_elf_image_phdr() and load_elf_image_shdr()
[platform/kernel/u-boot.git] / common / cmd_elf.c
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
22 /*
23  * A very simple elf loader, assumes the image is valid, returns the
24  * entry point address.
25  */
26 static unsigned long load_elf_image_phdr(unsigned long addr)
27 {
28         Elf32_Ehdr *ehdr; /* Elf header structure pointer */
29         Elf32_Phdr *phdr; /* Program header structure pointer */
30         int i;
31
32         ehdr = (Elf32_Ehdr *)addr;
33         phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff);
34
35         /* Load each program header */
36         for (i = 0; i < ehdr->e_phnum; ++i) {
37                 void *dst = (void *)(uintptr_t)phdr->p_paddr;
38                 void *src = (void *)addr + phdr->p_offset;
39                 debug("Loading phdr %i to 0x%p (%i bytes)\n",
40                       i, dst, phdr->p_filesz);
41                 if (phdr->p_filesz)
42                         memcpy(dst, src, phdr->p_filesz);
43                 if (phdr->p_filesz != phdr->p_memsz)
44                         memset(dst + phdr->p_filesz, 0x00,
45                                phdr->p_memsz - phdr->p_filesz);
46                 flush_cache((unsigned long)dst, phdr->p_filesz);
47                 ++phdr;
48         }
49
50         return ehdr->e_entry;
51 }
52
53 static unsigned long load_elf_image_shdr(unsigned long addr)
54 {
55         Elf32_Ehdr *ehdr; /* Elf header structure pointer */
56         Elf32_Shdr *shdr; /* Section header structure pointer */
57         unsigned char *strtab = 0; /* String table pointer */
58         unsigned char *image; /* Binary image pointer */
59         int i; /* Loop counter */
60
61         ehdr = (Elf32_Ehdr *)addr;
62
63         /* Find the section header string table for output info */
64         shdr = (Elf32_Shdr *)(addr + ehdr->e_shoff +
65                              (ehdr->e_shstrndx * sizeof(Elf32_Shdr)));
66
67         if (shdr->sh_type == SHT_STRTAB)
68                 strtab = (unsigned char *)(addr + shdr->sh_offset);
69
70         /* Load each appropriate section */
71         for (i = 0; i < ehdr->e_shnum; ++i) {
72                 shdr = (Elf32_Shdr *)(addr + ehdr->e_shoff +
73                                      (i * sizeof(Elf32_Shdr)));
74
75                 if (!(shdr->sh_flags & SHF_ALLOC) ||
76                     shdr->sh_addr == 0 || shdr->sh_size == 0) {
77                         continue;
78                 }
79
80                 if (strtab) {
81                         debug("%sing %s @ 0x%08lx (%ld bytes)\n",
82                               (shdr->sh_type == SHT_NOBITS) ? "Clear" : "Load",
83                                &strtab[shdr->sh_name],
84                                (unsigned long)shdr->sh_addr,
85                                (long)shdr->sh_size);
86                 }
87
88                 if (shdr->sh_type == SHT_NOBITS) {
89                         memset((void *)(uintptr_t)shdr->sh_addr, 0,
90                                shdr->sh_size);
91                 } else {
92                         image = (unsigned char *)addr + shdr->sh_offset;
93                         memcpy((void *)(uintptr_t)shdr->sh_addr,
94                                (const void *)image, shdr->sh_size);
95                 }
96                 flush_cache(shdr->sh_addr, shdr->sh_size);
97         }
98
99         return ehdr->e_entry;
100 }
101
102 /* Allow ports to override the default behavior */
103 static unsigned long do_bootelf_exec(ulong (*entry)(int, char * const[]),
104                                      int argc, char * const argv[])
105 {
106         unsigned long ret;
107
108         /*
109          * QNX images require the data cache is disabled.
110          * Data cache is already flushed, so just turn it off.
111          */
112         int dcache = dcache_status();
113         if (dcache)
114                 dcache_disable();
115
116         /*
117          * pass address parameter as argv[0] (aka command name),
118          * and all remaining args
119          */
120         ret = entry(argc, argv);
121
122         if (dcache)
123                 dcache_enable();
124
125         return ret;
126 }
127
128 /*
129  * Determine if a valid ELF image exists at the given memory location.
130  * First look at the ELF header magic field, then make sure that it is
131  * executable.
132  */
133 int valid_elf_image(unsigned long addr)
134 {
135         Elf32_Ehdr *ehdr; /* Elf header structure pointer */
136
137         ehdr = (Elf32_Ehdr *)addr;
138
139         if (!IS_ELF(*ehdr)) {
140                 printf("## No elf image at address 0x%08lx\n", addr);
141                 return 0;
142         }
143
144         if (ehdr->e_type != ET_EXEC) {
145                 printf("## Not a 32-bit elf image at address 0x%08lx\n", addr);
146                 return 0;
147         }
148
149         return 1;
150 }
151
152 /* Interpreter command to boot an arbitrary ELF image from memory */
153 int do_bootelf(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
154 {
155         unsigned long addr; /* Address of the ELF image */
156         unsigned long rc; /* Return value from user code */
157         char *sload, *saddr;
158         const char *ep = getenv("autostart");
159
160         int rcode = 0;
161
162         sload = saddr = NULL;
163         if (argc == 3) {
164                 sload = argv[1];
165                 saddr = argv[2];
166         } else if (argc == 2) {
167                 if (argv[1][0] == '-')
168                         sload = argv[1];
169                 else
170                         saddr = argv[1];
171         }
172
173         if (saddr)
174                 addr = simple_strtoul(saddr, NULL, 16);
175         else
176                 addr = load_addr;
177
178         if (!valid_elf_image(addr))
179                 return 1;
180
181         if (sload && sload[1] == 'p')
182                 addr = load_elf_image_phdr(addr);
183         else
184                 addr = load_elf_image_shdr(addr);
185
186         if (ep && !strcmp(ep, "no"))
187                 return rcode;
188
189         printf("## Starting application at 0x%08lx ...\n", addr);
190
191         /*
192          * pass address parameter as argv[0] (aka command name),
193          * and all remaining args
194          */
195         rc = do_bootelf_exec((void *)addr, argc - 1, argv + 1);
196         if (rc != 0)
197                 rcode = 1;
198
199         printf("## Application terminated, rc = 0x%lx\n", rc);
200
201         return rcode;
202 }
203
204 /*
205  * Interpreter command to boot VxWorks from a memory image.  The image can
206  * be either an ELF image or a raw binary.  Will attempt to setup the
207  * bootline and other parameters correctly.
208  */
209 int do_bootvx(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
210 {
211         unsigned long addr; /* Address of image */
212         unsigned long bootaddr; /* Address to put the bootline */
213         char *bootline; /* Text of the bootline */
214         char *tmp; /* Temporary char pointer */
215         char build_buf[128]; /* Buffer for building the bootline */
216
217         /*
218          * Check the loadaddr variable.
219          * If we don't know where the image is then we're done.
220          */
221         if (argc < 2)
222                 addr = load_addr;
223         else
224                 addr = simple_strtoul(argv[1], NULL, 16);
225
226 #if defined(CONFIG_CMD_NET)
227         /*
228          * Check to see if we need to tftp the image ourselves
229          * before starting
230          */
231         if ((argc == 2) && (strcmp(argv[1], "tftp") == 0)) {
232                 if (net_loop(TFTPGET) <= 0)
233                         return 1;
234                 printf("Automatic boot of VxWorks image at address 0x%08lx ...\n",
235                         addr);
236         }
237 #endif
238
239         /*
240          * This should equate to
241          * NV_RAM_ADRS + NV_BOOT_OFFSET + NV_ENET_OFFSET
242          * from the VxWorks BSP header files.
243          * This will vary from board to board
244          */
245 #if defined(CONFIG_WALNUT)
246         tmp = (char *)CONFIG_SYS_NVRAM_BASE_ADDR + 0x500;
247         eth_getenv_enetaddr("ethaddr", (uchar *)build_buf);
248         memcpy(tmp, &build_buf[3], 3);
249 #elif defined(CONFIG_SYS_VXWORKS_MAC_PTR)
250         tmp = (char *)CONFIG_SYS_VXWORKS_MAC_PTR;
251         eth_getenv_enetaddr("ethaddr", (uchar *)build_buf);
252         memcpy(tmp, build_buf, 6);
253 #else
254         puts("## Ethernet MAC address not copied to NV RAM\n");
255 #endif
256
257         /*
258          * Use bootaddr to find the location in memory that VxWorks
259          * will look for the bootline string. The default value for
260          * PowerPC is LOCAL_MEM_LOCAL_ADRS + BOOT_LINE_OFFSET which
261          * defaults to 0x4200.
262          */
263         tmp = getenv("bootaddr");
264         if (!tmp)
265                 bootaddr = CONFIG_SYS_VXWORKS_BOOT_ADDR;
266         else
267                 bootaddr = simple_strtoul(tmp, NULL, 16);
268
269         /*
270          * Check to see if the bootline is defined in the 'bootargs'
271          * parameter. If it is not defined, we may be able to
272          * construct the info.
273          */
274         bootline = getenv("bootargs");
275         if (bootline) {
276                 memcpy((void *)bootaddr, bootline,
277                        max(strlen(bootline), (size_t)255));
278                 flush_cache(bootaddr, max(strlen(bootline), (size_t)255));
279         } else {
280                 sprintf(build_buf, CONFIG_SYS_VXWORKS_BOOT_DEVICE);
281                 tmp = getenv("bootfile");
282                 if (tmp)
283                         sprintf(&build_buf[strlen(build_buf)],
284                                 "%s:%s ", CONFIG_SYS_VXWORKS_SERVERNAME, tmp);
285                 else
286                         sprintf(&build_buf[strlen(build_buf)],
287                                 "%s:file ", CONFIG_SYS_VXWORKS_SERVERNAME);
288
289                 tmp = getenv("ipaddr");
290                 if (tmp)
291                         sprintf(&build_buf[strlen(build_buf)], "e=%s ", tmp);
292
293                 tmp = getenv("serverip");
294                 if (tmp)
295                         sprintf(&build_buf[strlen(build_buf)], "h=%s ", tmp);
296
297                 tmp = getenv("hostname");
298                 if (tmp)
299                         sprintf(&build_buf[strlen(build_buf)], "tn=%s ", tmp);
300
301 #ifdef CONFIG_SYS_VXWORKS_ADD_PARAMS
302                 sprintf(&build_buf[strlen(build_buf)],
303                         CONFIG_SYS_VXWORKS_ADD_PARAMS);
304 #endif
305
306                 memcpy((void *)bootaddr, build_buf,
307                        max(strlen(build_buf), (size_t)255));
308                 flush_cache(bootaddr, max(strlen(build_buf), (size_t)255));
309         }
310
311         /*
312          * If the data at the load address is an elf image, then
313          * treat it like an elf image. Otherwise, assume that it is a
314          * binary image.
315          */
316         if (valid_elf_image(addr))
317                 addr = load_elf_image_shdr(addr);
318         else
319                 puts("## Not an ELF image, assuming binary\n");
320
321         printf("## Using bootline (@ 0x%lx): %s\n", bootaddr,
322                (char *)bootaddr);
323         printf("## Starting vxWorks at 0x%08lx ...\n", addr);
324
325         dcache_disable();
326         ((void (*)(int))addr)(0);
327
328         puts("## vxWorks terminated\n");
329
330         return 1;
331 }
332
333 U_BOOT_CMD(
334         bootelf, 3, 0, do_bootelf,
335         "Boot from an ELF image in memory",
336         "[-p|-s] [address]\n"
337         "\t- load ELF image at [address] via program headers (-p)\n"
338         "\t  or via section headers (-s)"
339 );
340
341 U_BOOT_CMD(
342         bootvx, 2, 0, do_bootvx,
343         "Boot vxWorks from an ELF image",
344         " [address] - load address of vxWorks ELF image."
345 );