elf.c 10.3 KB
Newer Older
wdenk's avatar
wdenk committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * Copyright (c) 2001 William L. Pitts
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are freely
 * permitted provided that the above copyright notice and this
 * paragraph and the following disclaimer are duplicated in all
 * such forms.
 *
 * This software is provided "AS IS" and without any express or
 * implied warranties, including, without limitation, the implied
 * warranties of merchantability and fitness for a particular
 * purpose.
 */

#include <common.h>
#include <command.h>
#include <elf.h>
19
#include <net.h>
20
#include <vxworks.h>
21 22
#ifdef CONFIG_X86
#include <asm/e820.h>
23
#include <linux/linkage.h>
24
#endif
wdenk's avatar
wdenk committed
25

26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
/*
 * A very simple elf loader, assumes the image is valid, returns the
 * entry point address.
 */
static unsigned long load_elf_image_phdr(unsigned long addr)
{
	Elf32_Ehdr *ehdr; /* Elf header structure pointer */
	Elf32_Phdr *phdr; /* Program header structure pointer */
	int i;

	ehdr = (Elf32_Ehdr *)addr;
	phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff);

	/* Load each program header */
	for (i = 0; i < ehdr->e_phnum; ++i) {
		void *dst = (void *)(uintptr_t)phdr->p_paddr;
		void *src = (void *)addr + phdr->p_offset;
		debug("Loading phdr %i to 0x%p (%i bytes)\n",
		      i, dst, phdr->p_filesz);
		if (phdr->p_filesz)
			memcpy(dst, src, phdr->p_filesz);
		if (phdr->p_filesz != phdr->p_memsz)
			memset(dst + phdr->p_filesz, 0x00,
			       phdr->p_memsz - phdr->p_filesz);
		flush_cache((unsigned long)dst, phdr->p_filesz);
		++phdr;
	}

	return ehdr->e_entry;
}

static unsigned long load_elf_image_shdr(unsigned long addr)
{
	Elf32_Ehdr *ehdr; /* Elf header structure pointer */
	Elf32_Shdr *shdr; /* Section header structure pointer */
	unsigned char *strtab = 0; /* String table pointer */
	unsigned char *image; /* Binary image pointer */
	int i; /* Loop counter */

	ehdr = (Elf32_Ehdr *)addr;

	/* Find the section header string table for output info */
	shdr = (Elf32_Shdr *)(addr + ehdr->e_shoff +
			     (ehdr->e_shstrndx * sizeof(Elf32_Shdr)));

	if (shdr->sh_type == SHT_STRTAB)
		strtab = (unsigned char *)(addr + shdr->sh_offset);

	/* Load each appropriate section */
	for (i = 0; i < ehdr->e_shnum; ++i) {
		shdr = (Elf32_Shdr *)(addr + ehdr->e_shoff +
				     (i * sizeof(Elf32_Shdr)));

		if (!(shdr->sh_flags & SHF_ALLOC) ||
		    shdr->sh_addr == 0 || shdr->sh_size == 0) {
			continue;
		}

		if (strtab) {
			debug("%sing %s @ 0x%08lx (%ld bytes)\n",
			      (shdr->sh_type == SHT_NOBITS) ? "Clear" : "Load",
			       &strtab[shdr->sh_name],
			       (unsigned long)shdr->sh_addr,
			       (long)shdr->sh_size);
		}

		if (shdr->sh_type == SHT_NOBITS) {
			memset((void *)(uintptr_t)shdr->sh_addr, 0,
			       shdr->sh_size);
		} else {
			image = (unsigned char *)addr + shdr->sh_offset;
			memcpy((void *)(uintptr_t)shdr->sh_addr,
			       (const void *)image, shdr->sh_size);
		}
		flush_cache(shdr->sh_addr, shdr->sh_size);
	}

	return ehdr->e_entry;
}
105 106

/* Allow ports to override the default behavior */
107
static unsigned long do_bootelf_exec(ulong (*entry)(int, char * const[]),
108
				     int argc, char * const argv[])
109
{
110 111
	unsigned long ret;

112 113 114 115
	/*
	 * QNX images require the data cache is disabled.
	 * Data cache is already flushed, so just turn it off.
	 */
116
	int dcache = dcache_status();
117
	if (dcache)
118
		dcache_disable();
119

120 121 122 123
	/*
	 * pass address parameter as argv[0] (aka command name),
	 * and all remaining args
	 */
124
	ret = entry(argc, argv);
125

126
	if (dcache)
127
		dcache_enable();
128 129 130

	return ret;
}
wdenk's avatar
wdenk committed
131

132
/*
133
 * Determine if a valid ELF image exists at the given memory location.
134 135 136
 * First look at the ELF header magic field, then make sure that it is
 * executable.
 */
137 138
int valid_elf_image(unsigned long addr)
{
139
	Elf32_Ehdr *ehdr; /* Elf header structure pointer */
140

141
	ehdr = (Elf32_Ehdr *)addr;
142 143 144 145 146 147 148 149 150 151 152 153 154 155

	if (!IS_ELF(*ehdr)) {
		printf("## No elf image at address 0x%08lx\n", addr);
		return 0;
	}

	if (ehdr->e_type != ET_EXEC) {
		printf("## Not a 32-bit elf image at address 0x%08lx\n", addr);
		return 0;
	}

	return 1;
}

156
/* Interpreter command to boot an arbitrary ELF image from memory */
157
int do_bootelf(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
wdenk's avatar
wdenk committed
158
{
159 160
	unsigned long addr; /* Address of the ELF image */
	unsigned long rc; /* Return value from user code */
161
	char *sload, *saddr;
162
	const char *ep = getenv("autostart");
wdenk's avatar
wdenk committed
163 164 165

	int rcode = 0;

166 167 168 169 170 171 172 173 174 175 176 177 178
	sload = saddr = NULL;
	if (argc == 3) {
		sload = argv[1];
		saddr = argv[2];
	} else if (argc == 2) {
		if (argv[1][0] == '-')
			sload = argv[1];
		else
			saddr = argv[1];
	}

	if (saddr)
		addr = simple_strtoul(saddr, NULL, 16);
wdenk's avatar
wdenk committed
179
	else
180
		addr = load_addr;
wdenk's avatar
wdenk committed
181

182
	if (!valid_elf_image(addr))
wdenk's avatar
wdenk committed
183 184
		return 1;

185 186 187 188
	if (sload && sload[1] == 'p')
		addr = load_elf_image_phdr(addr);
	else
		addr = load_elf_image_shdr(addr);
wdenk's avatar
wdenk committed
189

190 191 192
	if (ep && !strcmp(ep, "no"))
		return rcode;

193
	printf("## Starting application at 0x%08lx ...\n", addr);
wdenk's avatar
wdenk committed
194 195 196 197 198

	/*
	 * pass address parameter as argv[0] (aka command name),
	 * and all remaining args
	 */
199
	rc = do_bootelf_exec((void *)addr, argc - 1, argv + 1);
wdenk's avatar
wdenk committed
200 201 202
	if (rc != 0)
		rcode = 1;

203
	printf("## Application terminated, rc = 0x%lx\n", rc);
204

wdenk's avatar
wdenk committed
205 206 207
	return rcode;
}

208
/*
wdenk's avatar
wdenk committed
209 210 211
 * Interpreter command to boot VxWorks from a memory image.  The image can
 * be either an ELF image or a raw binary.  Will attempt to setup the
 * bootline and other parameters correctly.
212
 */
213
int do_bootvx(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
wdenk's avatar
wdenk committed
214
{
215 216 217 218 219
	unsigned long addr; /* Address of image */
	unsigned long bootaddr; /* Address to put the bootline */
	char *bootline; /* Text of the bootline */
	char *tmp; /* Temporary char pointer */
	char build_buf[128]; /* Buffer for building the bootline */
220
	int ptr = 0;
221 222 223 224
#ifdef CONFIG_X86
	struct e820info *info;
	struct e820entry *data;
#endif
225 226

	/*
wdenk's avatar
wdenk committed
227 228 229
	 * Check the loadaddr variable.
	 * If we don't know where the image is then we're done.
	 */
230
	if (argc < 2)
231 232
		addr = load_addr;
	else
233
		addr = simple_strtoul(argv[1], NULL, 16);
wdenk's avatar
wdenk committed
234

235
#if defined(CONFIG_CMD_NET)
236
	/*
237 238
	 * Check to see if we need to tftp the image ourselves
	 * before starting
239
	 */
240
	if ((argc == 2) && (strcmp(argv[1], "tftp") == 0)) {
241
		if (net_loop(TFTPGET) <= 0)
wdenk's avatar
wdenk committed
242
			return 1;
243 244
		printf("Automatic boot of VxWorks image at address 0x%08lx ...\n",
			addr);
wdenk's avatar
wdenk committed
245 246 247
	}
#endif

248 249 250
	/*
	 * This should equate to
	 * NV_RAM_ADRS + NV_BOOT_OFFSET + NV_ENET_OFFSET
wdenk's avatar
wdenk committed
251 252 253
	 * from the VxWorks BSP header files.
	 * This will vary from board to board
	 */
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);
wdenk's avatar
wdenk committed
262
#else
263
	puts("## Ethernet MAC address not copied to NV RAM\n");
wdenk's avatar
wdenk committed
264 265
#endif

wdenk's avatar
wdenk committed
266 267
	/*
	 * Use bootaddr to find the location in memory that VxWorks
268 269 270
	 * will look for the bootline string. The default value is
	 * (LOCAL_MEM_LOCAL_ADRS + BOOT_LINE_OFFSET) as defined by
	 * VxWorks BSP. For example, on PowerPC it defaults to 0x4200.
wdenk's avatar
wdenk committed
271
	 */
272
	tmp = getenv("bootaddr");
273 274
	if (!tmp) {
		printf("## VxWorks bootline address not specified\n");
wdenk's avatar
wdenk committed
275
	} else {
276
		bootaddr = simple_strtoul(tmp, NULL, 16);
wdenk's avatar
wdenk committed
277

278 279 280 281 282 283 284 285 286 287 288 289 290
		/*
		 * Check to see if the bootline is defined in the 'bootargs'
		 * parameter. If it is not defined, we may be able to
		 * construct the info.
		 */
		bootline = getenv("bootargs");
		if (bootline) {
			memcpy((void *)bootaddr, bootline,
			       max(strlen(bootline), (size_t)255));
			flush_cache(bootaddr, max(strlen(bootline),
						  (size_t)255));
		} else {
			tmp = getenv("bootdev");
291 292 293 294
			if (tmp) {
				strcpy(build_buf, tmp);
				ptr = strlen(tmp);
			} else
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
				printf("## VxWorks boot device not specified\n");

			tmp = getenv("bootfile");
			if (tmp)
				ptr += sprintf(build_buf + ptr,
					       "host:%s ", tmp);
			else
				ptr += sprintf(build_buf + ptr,
					       "host:vxWorks ");

			/*
			 * The following parameters are only needed if 'bootdev'
			 * is an ethernet device, otherwise they are optional.
			 */
			tmp = getenv("ipaddr");
310
			if (tmp) {
311 312 313 314 315 316 317 318 319
				ptr += sprintf(build_buf + ptr, "e=%s", tmp);
				tmp = getenv("netmask");
				if (tmp) {
					u32 mask = getenv_ip("netmask").s_addr;
					ptr += sprintf(build_buf + ptr,
						       ":%08x ", ntohl(mask));
				} else {
					ptr += sprintf(build_buf + ptr, " ");
				}
320
			}
wdenk's avatar
wdenk committed
321

322 323 324
			tmp = getenv("serverip");
			if (tmp)
				ptr += sprintf(build_buf + ptr, "h=%s ", tmp);
325

326 327 328
			tmp = getenv("gatewayip");
			if (tmp)
				ptr += sprintf(build_buf + ptr, "g=%s ", tmp);
329

330 331 332
			tmp = getenv("hostname");
			if (tmp)
				ptr += sprintf(build_buf + ptr, "tn=%s ", tmp);
wdenk's avatar
wdenk committed
333

334
			tmp = getenv("othbootargs");
335 336 337 338
			if (tmp) {
				strcpy(build_buf + ptr, tmp);
				ptr += strlen(tmp);
			}
339 340 341 342 343 344

			memcpy((void *)bootaddr, build_buf,
			       max(strlen(build_buf), (size_t)255));
			flush_cache(bootaddr, max(strlen(build_buf),
						  (size_t)255));
		}
345

346 347
		printf("## Using bootline (@ 0x%lx): %s\n", bootaddr,
		       (char *)bootaddr);
wdenk's avatar
wdenk committed
348 349
	}

350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
#ifdef CONFIG_X86
	/*
	 * Since E820 information is critical to the kernel, if we don't
	 * specify these in the environments, use a default one.
	 */
	tmp = getenv("e820data");
	if (tmp)
		data = (struct e820entry *)simple_strtoul(tmp, NULL, 16);
	else
		data = (struct e820entry *)VXWORKS_E820_DATA_ADDR;
	tmp = getenv("e820info");
	if (tmp)
		info = (struct e820info *)simple_strtoul(tmp, NULL, 16);
	else
		info = (struct e820info *)VXWORKS_E820_INFO_ADDR;

	memset(info, 0, sizeof(struct e820info));
	info->sign = E820_SIGNATURE;
	info->entries = install_e820_map(E820MAX, data);
	info->addr = (info->entries - 1) * sizeof(struct e820entry) +
		     VXWORKS_E820_DATA_ADDR;
#endif

wdenk's avatar
wdenk committed
373 374 375
	/*
	 * If the data at the load address is an elf image, then
	 * treat it like an elf image. Otherwise, assume that it is a
376
	 * binary image.
wdenk's avatar
wdenk committed
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");
wdenk's avatar
wdenk committed
382

383
	printf("## Starting vxWorks at 0x%08lx ...\n", addr);
wdenk's avatar
wdenk committed
384

385
	dcache_disable();
386 387 388 389
#ifdef CONFIG_X86
	/* VxWorks on x86 uses stack to pass parameters */
	((asmlinkage void (*)(int))addr)(0);
#else
390
	((void (*)(int))addr)(0);
391
#endif
wdenk's avatar
wdenk committed
392

393
	puts("## vxWorks terminated\n");
394

wdenk's avatar
wdenk committed
395 396 397
	return 1;
}

398
U_BOOT_CMD(
399
	bootelf, 3, 0, do_bootelf,
Peter Tyser's avatar
Peter Tyser committed
400
	"Boot from an ELF image in memory",
401 402 403
	"[-p|-s] [address]\n"
	"\t- load ELF image at [address] via program headers (-p)\n"
	"\t  or via section headers (-s)"
wdenk's avatar
wdenk committed
404 405
);

406
U_BOOT_CMD(
407
	bootvx, 2, 0, do_bootvx,
Peter Tyser's avatar
Peter Tyser committed
408
	"Boot vxWorks from an ELF image",
409
	" [address] - load address of vxWorks ELF image."
wdenk's avatar
wdenk committed
410
);