cmd_elf.c 10.1 KB
Newer Older
wdenk's avatar
wdenk committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * 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>
17
#include <bootm.h>
wdenk's avatar
wdenk committed
18 19 20 21
#include <command.h>
#include <linux/ctype.h>
#include <net.h>
#include <elf.h>
22
#include <vxworks.h>
wdenk's avatar
wdenk committed
23

24
#if defined(CONFIG_WALNUT) || defined(CONFIG_SYS_VXWORKS_MAC_PTR)
25 26
DECLARE_GLOBAL_DATA_PTR;
#endif
wdenk's avatar
wdenk committed
27

28 29
static unsigned long load_elf_image_phdr(unsigned long addr);
static unsigned long load_elf_image_shdr(unsigned long addr);
30 31

/* Allow ports to override the default behavior */
32
static unsigned long do_bootelf_exec(ulong (*entry)(int, char * const[]),
33
			       int argc, char * const argv[])
34
{
35 36
	unsigned long ret;

37 38 39 40
	/*
	 * QNX images require the data cache is disabled.
	 * Data cache is already flushed, so just turn it off.
	 */
41
	int dcache = dcache_status();
42
	if (dcache)
43
		dcache_disable();
44

45 46 47 48
	/*
	 * pass address parameter as argv[0] (aka command name),
	 * and all remaining args
	 */
49
	ret = entry(argc, argv);
50

51
	if (dcache)
52
		dcache_enable();
53 54 55

	return ret;
}
wdenk's avatar
wdenk committed
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
/* ======================================================================
 * Determine if a valid ELF image exists at the given memory location.
 * First looks at the ELF header magic field, the makes sure that it is
 * executable and makes sure that it is for a PowerPC.
 * ====================================================================== */
int valid_elf_image(unsigned long addr)
{
	Elf32_Ehdr *ehdr;		/* Elf header structure pointer */

	/* -------------------------------------------------- */

	ehdr = (Elf32_Ehdr *) addr;

	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;
	}

#if 0
	if (ehdr->e_machine != EM_PPC) {
		printf("## Not a PowerPC elf image at address 0x%08lx\n", addr);
		return 0;
	}
#endif

	return 1;
}

wdenk's avatar
wdenk committed
90 91 92
/* ======================================================================
 * Interpreter command to boot an arbitrary ELF image from memory.
 * ====================================================================== */
93
int do_bootelf(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
wdenk's avatar
wdenk committed
94 95 96
{
	unsigned long addr;		/* Address of the ELF image     */
	unsigned long rc;		/* Return value from user code  */
97
	char *sload, *saddr;
wdenk's avatar
wdenk committed
98 99 100 101

	/* -------------------------------------------------- */
	int rcode = 0;

102 103 104 105 106 107 108 109 110 111 112 113 114
	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
115
	else
116
		addr = load_addr;
wdenk's avatar
wdenk committed
117

118
	if (!valid_elf_image(addr))
wdenk's avatar
wdenk committed
119 120
		return 1;

121 122 123 124
	if (sload && sload[1] == 'p')
		addr = load_elf_image_phdr(addr);
	else
		addr = load_elf_image_shdr(addr);
wdenk's avatar
wdenk committed
125

126
	printf("## Starting application at 0x%08lx ...\n", addr);
wdenk's avatar
wdenk committed
127 128 129 130 131

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

136
	printf("## Application terminated, rc = 0x%lx\n", rc);
wdenk's avatar
wdenk committed
137 138 139 140 141 142 143 144
	return rcode;
}

/* ======================================================================
 * 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.
 * ====================================================================== */
145
int do_bootvx(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
wdenk's avatar
wdenk committed
146 147
{
	unsigned long addr;		/* Address of image            */
148
	unsigned long bootaddr;	/* Address to put the bootline */
wdenk's avatar
wdenk committed
149 150
	char *bootline;			/* Text of the bootline        */
	char *tmp;			/* Temporary char pointer      */
151
	char build_buf[128];		/* Buffer for building the bootline */
wdenk's avatar
wdenk committed
152

153 154
	/* ---------------------------------------------------
	 *
wdenk's avatar
wdenk committed
155 156 157 158
	 * Check the loadaddr variable.
	 * If we don't know where the image is then we're done.
	 */

159
	if (argc < 2)
160 161
		addr = load_addr;
	else
162
		addr = simple_strtoul(argv[1], NULL, 16);
wdenk's avatar
wdenk committed
163

164
#if defined(CONFIG_CMD_NET)
165 166 167
	/*
	 * Check to see if we need to tftp the image ourselves before starting
	 */
168
	if ((argc == 2) && (strcmp(argv[1], "tftp") == 0)) {
169
		if (NetLoop(TFTPGET) <= 0)
wdenk's avatar
wdenk committed
170
			return 1;
171 172
		printf("Automatic boot of VxWorks image at address 0x%08lx ...\n",
			addr);
wdenk's avatar
wdenk committed
173 174 175 176 177 178 179 180 181
	}
#endif

	/* This should equate
	 * to NV_RAM_ADRS + NV_BOOT_OFFSET + NV_ENET_OFFSET
	 * from the VxWorks BSP header files.
	 * This will vary from board to board
	 */

182
#if defined(CONFIG_WALNUT)
183
	tmp = (char *) CONFIG_SYS_NVRAM_BASE_ADDR + 0x500;
184
	eth_getenv_enetaddr("ethaddr", (uchar *)build_buf);
185
	memcpy(tmp, &build_buf[3], 3);
186 187
#elif defined(CONFIG_SYS_VXWORKS_MAC_PTR)
	tmp = (char *) CONFIG_SYS_VXWORKS_MAC_PTR;
188
	eth_getenv_enetaddr("ethaddr", (uchar *)build_buf);
189
	memcpy(tmp, build_buf, 6);
wdenk's avatar
wdenk committed
190
#else
191
	puts("## Ethernet MAC address not copied to NV RAM\n");
wdenk's avatar
wdenk committed
192 193
#endif

wdenk's avatar
wdenk committed
194 195 196 197 198
	/*
	 * Use bootaddr to find the location in memory that VxWorks
	 * will look for the bootline string. The default value for
	 * PowerPC is LOCAL_MEM_LOCAL_ADRS + BOOT_LINE_OFFSET which
	 * defaults to 0x4200
wdenk's avatar
wdenk committed
199
	 */
200
	tmp = getenv("bootaddr");
201
	if (!tmp)
202
		bootaddr = CONFIG_SYS_VXWORKS_BOOT_ADDR;
wdenk's avatar
wdenk committed
203
	else
204
		bootaddr = simple_strtoul(tmp, NULL, 16);
wdenk's avatar
wdenk committed
205

wdenk's avatar
wdenk committed
206 207 208 209
	/*
	 * 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
wdenk's avatar
wdenk committed
210
	 */
211 212
	bootline = getenv("bootargs");
	if (bootline) {
213 214 215
		memcpy((void *)bootaddr, bootline,
		       max(strlen(bootline), (size_t)255));
		flush_cache(bootaddr, max(strlen(bootline), (size_t)255));
wdenk's avatar
wdenk committed
216
	} else {
217 218 219 220
		sprintf(build_buf, CONFIG_SYS_VXWORKS_BOOT_DEVICE);
		tmp = getenv("bootfile");
		if (tmp)
			sprintf(&build_buf[strlen(build_buf)],
221
				 "%s:%s ", CONFIG_SYS_VXWORKS_SERVERNAME, tmp);
222 223
		else
			sprintf(&build_buf[strlen(build_buf)],
224
				 "%s:file ", CONFIG_SYS_VXWORKS_SERVERNAME);
wdenk's avatar
wdenk committed
225

226 227 228
		tmp = getenv("ipaddr");
		if (tmp)
			sprintf(&build_buf[strlen(build_buf)], "e=%s ", tmp);
wdenk's avatar
wdenk committed
229

230 231 232 233 234 235 236
		tmp = getenv("serverip");
		if (tmp)
			sprintf(&build_buf[strlen(build_buf)], "h=%s ", tmp);

		tmp = getenv("hostname");
		if (tmp)
			sprintf(&build_buf[strlen(build_buf)], "tn=%s ", tmp);
wdenk's avatar
wdenk committed
237

238
#ifdef CONFIG_SYS_VXWORKS_ADD_PARAMS
239
		sprintf(&build_buf[strlen(build_buf)],
240
			 CONFIG_SYS_VXWORKS_ADD_PARAMS);
wdenk's avatar
wdenk committed
241
#endif
242

243 244 245
		memcpy((void *)bootaddr, build_buf,
		       max(strlen(build_buf), (size_t)255));
		flush_cache(bootaddr, max(strlen(build_buf), (size_t)255));
wdenk's avatar
wdenk committed
246 247
	}

wdenk's avatar
wdenk committed
248 249 250 251
	/*
	 * If the data at the load address is an elf image, then
	 * treat it like an elf image. Otherwise, assume that it is a
	 * binary image
wdenk's avatar
wdenk committed
252 253
	 */

254 255
	if (valid_elf_image(addr)) {
		addr = load_elf_image_shdr(addr);
wdenk's avatar
wdenk committed
256
	} else {
257
		puts("## Not an ELF image, assuming binary\n");
wdenk's avatar
wdenk committed
258 259 260
		/* leave addr as load_addr */
	}

261
	printf("## Using bootline (@ 0x%lx): %s\n", bootaddr,
wdenk's avatar
wdenk committed
262
			(char *) bootaddr);
263
	printf("## Starting vxWorks at 0x%08lx ...\n", addr);
wdenk's avatar
wdenk committed
264

265 266
	dcache_disable();
	((void (*)(int)) addr) (0);
wdenk's avatar
wdenk committed
267

268
	puts("## vxWorks terminated\n");
wdenk's avatar
wdenk committed
269 270 271 272 273 274 275
	return 1;
}

/* ======================================================================
 * A very simple elf loader, assumes the image is valid, returns the
 * entry point address.
 * ====================================================================== */
276 277 278 279 280 281 282 283 284 285 286
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) {
287
		void *dst = (void *)(uintptr_t) phdr->p_paddr;
288 289 290 291 292 293
		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)
294 295
			memset(dst + phdr->p_filesz, 0x00,
				phdr->p_memsz - phdr->p_filesz);
296 297 298 299 300 301 302 303
		flush_cache((unsigned long)dst, phdr->p_filesz);
		++phdr;
	}

	return ehdr->e_entry;
}

static unsigned long load_elf_image_shdr(unsigned long addr)
wdenk's avatar
wdenk committed
304 305 306 307 308 309 310 311 312 313 314 315 316
{
	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 +
317
			       (ehdr->e_shstrndx * sizeof(Elf32_Shdr)));
wdenk's avatar
wdenk committed
318 319 320 321 322 323 324

	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 +
325
				       (i * sizeof(Elf32_Shdr)));
wdenk's avatar
wdenk committed
326 327 328 329 330 331 332

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

		if (strtab) {
333
			debug("%sing %s @ 0x%08lx (%ld bytes)\n",
wdenk's avatar
wdenk committed
334 335 336 337 338 339 340 341
				(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) {
342 343
			memset((void *)(uintptr_t) shdr->sh_addr, 0,
				shdr->sh_size);
wdenk's avatar
wdenk committed
344 345
		} else {
			image = (unsigned char *) addr + shdr->sh_offset;
346
			memcpy((void *)(uintptr_t) shdr->sh_addr,
wdenk's avatar
wdenk committed
347 348 349
				(const void *) image,
				shdr->sh_size);
		}
350
		flush_cache(shdr->sh_addr, shdr->sh_size);
wdenk's avatar
wdenk committed
351 352 353 354 355 356
	}

	return ehdr->e_entry;
}

/* ====================================================================== */
357
U_BOOT_CMD(
358
	bootelf,      3,      0,      do_bootelf,
Peter Tyser's avatar
Peter Tyser committed
359
	"Boot from an ELF image in memory",
360 361 362
	"[-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
363 364
);

365 366
U_BOOT_CMD(
	bootvx,      2,      0,      do_bootvx,
Peter Tyser's avatar
Peter Tyser committed
367
	"Boot vxWorks from an ELF image",
368
	" [address] - load address of vxWorks ELF image."
wdenk's avatar
wdenk committed
369
);