1// SPDX-License-Identifier: BSD-2-Clause
2/*
3 * Copyright (c) 2001 William L. Pitts
4 * All rights reserved.
5 */
6
7#include <common.h>
8#include <command.h>
9#include <cpu_func.h>
10#include <elf.h>
11#include <env.h>
12#include <image.h>
13#include <log.h>
14#include <net.h>
15#include <vxworks.h>
16#ifdef CONFIG_X86
17#include <vesa.h>
18#include <asm/cache.h>
19#include <asm/e820.h>
20#include <linux/linkage.h>
21#endif
22
23/* Allow ports to override the default behavior */
24static unsigned long do_bootelf_exec(ulong (*entry)(int, char * const[]),
25				     int argc, char *const argv[])
26{
27	unsigned long ret;
28
29	/*
30	 * pass address parameter as argv[0] (aka command name),
31	 * and all remaining args
32	 */
33	ret = entry(argc, argv);
34
35	return ret;
36}
37
38/* Interpreter command to boot an arbitrary ELF image from memory */
39int do_bootelf(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
40{
41#if CONFIG_IS_ENABLED(CMD_ELF_FDT_SETUP)
42	struct bootm_headers img = {0};
43	unsigned long fdt_addr = 0; /* Address of the FDT */
44#endif
45	unsigned long addr; /* Address of the ELF image */
46	unsigned long rc; /* Return value from user code */
47	char *sload = NULL;
48	int rcode = 0;
49
50	/* Consume 'bootelf' */
51	argc--; argv++;
52
53	/* Check for [-p|-s] flag. */
54	if (argc >= 1 && (argv[0][0] == '-' && \
55				(argv[0][1] == 'p' || argv[0][1] == 's'))) {
56		sload = argv[0];
57		/* Consume flag. */
58		argc--; argv++;
59	}
60
61#if CONFIG_IS_ENABLED(CMD_ELF_FDT_SETUP)
62	/* Check for [-d fdt_addr_r] option. */
63	if ((argc >= 2) && (argv[0][0] == '-') && (argv[0][1] == 'd')) {
64		if (strict_strtoul(argv[1], 16, &fdt_addr) != 0)
65			return CMD_RET_USAGE;
66		/* Consume option. */
67		argc -= 2;
68		argv += 2;
69	}
70#endif
71
72	/* Check for address. */
73	if (argc >= 1 && strict_strtoul(argv[0], 16, &addr) != -EINVAL) {
74		/* Consume address */
75		argc--; argv++;
76	} else
77		addr = image_load_addr;
78
79	if (!valid_elf_image(addr))
80		return 1;
81
82	if (sload && sload[1] == 'p')
83		addr = load_elf_image_phdr(addr);
84	else
85		addr = load_elf_image_shdr(addr);
86
87#if CONFIG_IS_ENABLED(CMD_ELF_FDT_SETUP)
88	if (fdt_addr) {
89		printf("## Setting up FDT at 0x%08lx ...\n", fdt_addr);
90		flush();
91
92		if (image_setup_libfdt(&img, (void *)fdt_addr, NULL))
93			return 1;
94	}
95#endif
96
97	if (!env_get_autostart())
98		return rcode;
99
100	printf("## Starting application at 0x%08lx ...\n", addr);
101	flush();
102
103	/*
104	 * pass address parameter as argv[0] (aka command name),
105	 * and all remaining args
106	 */
107	rc = do_bootelf_exec((void *)addr, argc, argv);
108	if (rc != 0)
109		rcode = 1;
110
111	printf("## Application terminated, rc = 0x%lx\n", rc);
112
113	return rcode;
114}
115
116/*
117 * Interpreter command to boot VxWorks from a memory image.  The image can
118 * be either an ELF image or a raw binary.  Will attempt to setup the
119 * bootline and other parameters correctly.
120 */
121int do_bootvx(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
122{
123	unsigned long addr; /* Address of image */
124	unsigned long bootaddr = 0; /* Address to put the bootline */
125	char *bootline; /* Text of the bootline */
126	char *tmp; /* Temporary char pointer */
127	char build_buf[128]; /* Buffer for building the bootline */
128	int ptr = 0;
129#ifdef CONFIG_X86
130	ulong base;
131	struct e820_info *info;
132	struct e820_entry *data;
133	struct efi_gop_info *gop;
134	struct vesa_mode_info *vesa = &mode_info.vesa;
135#endif
136
137	/*
138	 * Check the loadaddr variable.
139	 * If we don't know where the image is then we're done.
140	 */
141	if (argc < 2)
142		addr = image_load_addr;
143	else
144		addr = hextoul(argv[1], NULL);
145
146#if defined(CONFIG_CMD_NET)
147	/*
148	 * Check to see if we need to tftp the image ourselves
149	 * before starting
150	 */
151	if ((argc == 2) && (strcmp(argv[1], "tftp") == 0)) {
152		if (net_loop(TFTPGET) <= 0)
153			return 1;
154		printf("Automatic boot of VxWorks image at address 0x%08lx ...\n",
155			addr);
156	}
157#endif
158
159	/*
160	 * This should equate to
161	 * NV_RAM_ADRS + NV_BOOT_OFFSET + NV_ENET_OFFSET
162	 * from the VxWorks BSP header files.
163	 * This will vary from board to board
164	 */
165#if defined(CONFIG_SYS_VXWORKS_MAC_PTR)
166	tmp = (char *)CONFIG_SYS_VXWORKS_MAC_PTR;
167	eth_env_get_enetaddr("ethaddr", (uchar *)build_buf);
168	memcpy(tmp, build_buf, 6);
169#else
170	puts("## Ethernet MAC address not copied to NV RAM\n");
171#endif
172
173#ifdef CONFIG_X86
174	/*
175	 * Get VxWorks's physical memory base address from environment,
176	 * if we don't specify it in the environment, use a default one.
177	 */
178	base = env_get_hex("vx_phys_mem_base", VXWORKS_PHYS_MEM_BASE);
179	data = (struct e820_entry *)(base + E820_DATA_OFFSET);
180	info = (struct e820_info *)(base + E820_INFO_OFFSET);
181
182	memset(info, 0, sizeof(struct e820_info));
183	info->sign = E820_SIGNATURE;
184	info->entries = install_e820_map(E820MAX, data);
185	info->addr = (info->entries - 1) * sizeof(struct e820_entry) +
186		     E820_DATA_OFFSET;
187
188	/*
189	 * Explicitly clear the bootloader image size otherwise if memory
190	 * at this offset happens to contain some garbage data, the final
191	 * available memory size for the kernel is insane.
192	 */
193	*(u32 *)(base + BOOT_IMAGE_SIZE_OFFSET) = 0;
194
195	/*
196	 * Prepare compatible framebuffer information block.
197	 * The VESA mode has to be 32-bit RGBA.
198	 */
199	if (vesa->x_resolution && vesa->y_resolution) {
200		gop = (struct efi_gop_info *)(base + EFI_GOP_INFO_OFFSET);
201		gop->magic = EFI_GOP_INFO_MAGIC;
202		gop->info.version = 0;
203		gop->info.width = vesa->x_resolution;
204		gop->info.height = vesa->y_resolution;
205		gop->info.pixel_format = EFI_GOT_RGBA8;
206		gop->info.pixels_per_scanline = vesa->bytes_per_scanline / 4;
207		gop->fb_base = vesa->phys_base_ptr;
208		gop->fb_size = vesa->bytes_per_scanline * vesa->y_resolution;
209	}
210#endif
211
212	/*
213	 * Use bootaddr to find the location in memory that VxWorks
214	 * will look for the bootline string. The default value is
215	 * (LOCAL_MEM_LOCAL_ADRS + BOOT_LINE_OFFSET) as defined by
216	 * VxWorks BSP. For example, on PowerPC it defaults to 0x4200.
217	 */
218	tmp = env_get("bootaddr");
219	if (!tmp) {
220#ifdef CONFIG_X86
221		bootaddr = base + X86_BOOT_LINE_OFFSET;
222#else
223		printf("## VxWorks bootline address not specified\n");
224		return 1;
225#endif
226	}
227
228	if (!bootaddr)
229		bootaddr = hextoul(tmp, NULL);
230
231	/*
232	 * Check to see if the bootline is defined in the 'bootargs' parameter.
233	 * If it is not defined, we may be able to construct the info.
234	 */
235	bootline = env_get("bootargs");
236	if (!bootline) {
237		tmp = env_get("bootdev");
238		if (tmp) {
239			strcpy(build_buf, tmp);
240			ptr = strlen(tmp);
241		} else {
242			printf("## VxWorks boot device not specified\n");
243		}
244
245		tmp = env_get("bootfile");
246		if (tmp)
247			ptr += sprintf(build_buf + ptr, "host:%s ", tmp);
248		else
249			ptr += sprintf(build_buf + ptr, "host:vxWorks ");
250
251		/*
252		 * The following parameters are only needed if 'bootdev'
253		 * is an ethernet device, otherwise they are optional.
254		 */
255		tmp = env_get("ipaddr");
256		if (tmp) {
257			ptr += sprintf(build_buf + ptr, "e=%s", tmp);
258			tmp = env_get("netmask");
259			if (tmp) {
260				u32 mask = env_get_ip("netmask").s_addr;
261				ptr += sprintf(build_buf + ptr,
262					       ":%08x ", ntohl(mask));
263			} else {
264				ptr += sprintf(build_buf + ptr, " ");
265			}
266		}
267
268		tmp = env_get("serverip");
269		if (tmp)
270			ptr += sprintf(build_buf + ptr, "h=%s ", tmp);
271
272		tmp = env_get("gatewayip");
273		if (tmp)
274			ptr += sprintf(build_buf + ptr, "g=%s ", tmp);
275
276		tmp = env_get("hostname");
277		if (tmp)
278			ptr += sprintf(build_buf + ptr, "tn=%s ", tmp);
279
280		tmp = env_get("othbootargs");
281		if (tmp) {
282			strcpy(build_buf + ptr, tmp);
283			ptr += strlen(tmp);
284		}
285
286		bootline = build_buf;
287	}
288
289	memcpy((void *)bootaddr, bootline, max(strlen(bootline), (size_t)255));
290	flush_cache(bootaddr, max(strlen(bootline), (size_t)255));
291	printf("## Using bootline (@ 0x%lx): %s\n", bootaddr, (char *)bootaddr);
292
293	/*
294	 * If the data at the load address is an elf image, then
295	 * treat it like an elf image. Otherwise, assume that it is a
296	 * binary image.
297	 */
298	if (valid_elf_image(addr))
299		addr = load_elf_image_phdr(addr);
300	else
301		puts("## Not an ELF image, assuming binary\n");
302
303	printf("## Starting vxWorks at 0x%08lx ...\n", addr);
304	flush();
305
306	dcache_disable();
307#if defined(CONFIG_ARM64) && defined(CONFIG_ARMV8_PSCI)
308	armv8_setup_psci();
309	smp_kick_all_cpus();
310#endif
311
312#ifdef CONFIG_X86
313	/* VxWorks on x86 uses stack to pass parameters */
314	((asmlinkage void (*)(int))addr)(0);
315#else
316	((void (*)(int))addr)(0);
317#endif
318
319	puts("## vxWorks terminated\n");
320
321	return 1;
322}
323
324U_BOOT_CMD(
325	bootelf, CONFIG_SYS_MAXARGS, 0, do_bootelf,
326	"Boot from an ELF image in memory",
327	"[-p|-s] "
328#if CONFIG_IS_ENABLED(CMD_ELF_FDT_SETUP)
329	"[-d fdt_addr_r] "
330#endif
331	"[address]\n"
332	"\t- load ELF image at [address] via program headers (-p)\n"
333	"\t  or via section headers (-s)\n"
334#if CONFIG_IS_ENABLED(CMD_ELF_FDT_SETUP)
335	"\t- setup FDT image at [fdt_addr_r] (-d)"
336#endif
337);
338
339U_BOOT_CMD(
340	bootvx, 2, 0, do_bootvx,
341	"Boot vxWorks from an ELF image",
342	" [address] - load address of vxWorks ELF image."
343);
344