xref: /openbmc/u-boot/arch/arm/lib/bootm.c (revision 2e3f1ff63f50f36e74d46f939823241856ebf1bd)
1  /* Copyright (C) 2011
2   * Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de>
3   *  - Added prep subcommand support
4   *  - Reorganized source - modeled after powerpc version
5   *
6   * (C) Copyright 2002
7   * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
8   * Marius Groeger <mgroeger@sysgo.de>
9   *
10   * Copyright (C) 2001  Erik Mouw (J.A.K.Mouw@its.tudelft.nl)
11   *
12   * SPDX-License-Identifier:	GPL-2.0+
13   */
14  
15  #include <common.h>
16  #include <command.h>
17  #include <image.h>
18  #include <u-boot/zlib.h>
19  #include <asm/byteorder.h>
20  #include <libfdt.h>
21  #include <mapmem.h>
22  #include <fdt_support.h>
23  #include <asm/bootm.h>
24  #include <asm/secure.h>
25  #include <linux/compiler.h>
26  #include <bootm.h>
27  #include <vxworks.h>
28  
29  #ifdef CONFIG_ARMV7_NONSEC
30  #include <asm/armv7.h>
31  #endif
32  
33  DECLARE_GLOBAL_DATA_PTR;
34  
35  static struct tag *params;
36  
37  static ulong get_sp(void)
38  {
39  	ulong ret;
40  
41  	asm("mov %0, sp" : "=r"(ret) : );
42  	return ret;
43  }
44  
45  void arch_lmb_reserve(struct lmb *lmb)
46  {
47  	ulong sp;
48  
49  	/*
50  	 * Booting a (Linux) kernel image
51  	 *
52  	 * Allocate space for command line and board info - the
53  	 * address should be as high as possible within the reach of
54  	 * the kernel (see CONFIG_SYS_BOOTMAPSZ settings), but in unused
55  	 * memory, which means far enough below the current stack
56  	 * pointer.
57  	 */
58  	sp = get_sp();
59  	debug("## Current stack ends at 0x%08lx ", sp);
60  
61  	/* adjust sp by 4K to be safe */
62  	sp -= 4096;
63  	lmb_reserve(lmb, sp,
64  		    gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size - sp);
65  }
66  
67  /**
68   * announce_and_cleanup() - Print message and prepare for kernel boot
69   *
70   * @fake: non-zero to do everything except actually boot
71   */
72  static void announce_and_cleanup(int fake)
73  {
74  	printf("\nStarting kernel ...%s\n\n", fake ?
75  		"(fake run for tracing)" : "");
76  	bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
77  #ifdef CONFIG_BOOTSTAGE_FDT
78  	bootstage_fdt_add_report();
79  #endif
80  #ifdef CONFIG_BOOTSTAGE_REPORT
81  	bootstage_report();
82  #endif
83  
84  #ifdef CONFIG_USB_DEVICE
85  	udc_disconnect();
86  #endif
87  	cleanup_before_linux();
88  }
89  
90  static void setup_start_tag (bd_t *bd)
91  {
92  	params = (struct tag *)bd->bi_boot_params;
93  
94  	params->hdr.tag = ATAG_CORE;
95  	params->hdr.size = tag_size (tag_core);
96  
97  	params->u.core.flags = 0;
98  	params->u.core.pagesize = 0;
99  	params->u.core.rootdev = 0;
100  
101  	params = tag_next (params);
102  }
103  
104  static void setup_memory_tags(bd_t *bd)
105  {
106  	int i;
107  
108  	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
109  		params->hdr.tag = ATAG_MEM;
110  		params->hdr.size = tag_size (tag_mem32);
111  
112  		params->u.mem.start = bd->bi_dram[i].start;
113  		params->u.mem.size = bd->bi_dram[i].size;
114  
115  		params = tag_next (params);
116  	}
117  }
118  
119  static void setup_commandline_tag(bd_t *bd, char *commandline)
120  {
121  	char *p;
122  
123  	if (!commandline)
124  		return;
125  
126  	/* eat leading white space */
127  	for (p = commandline; *p == ' '; p++);
128  
129  	/* skip non-existent command lines so the kernel will still
130  	 * use its default command line.
131  	 */
132  	if (*p == '\0')
133  		return;
134  
135  	params->hdr.tag = ATAG_CMDLINE;
136  	params->hdr.size =
137  		(sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;
138  
139  	strcpy (params->u.cmdline.cmdline, p);
140  
141  	params = tag_next (params);
142  }
143  
144  static void setup_initrd_tag(bd_t *bd, ulong initrd_start, ulong initrd_end)
145  {
146  	/* an ATAG_INITRD node tells the kernel where the compressed
147  	 * ramdisk can be found. ATAG_RDIMG is a better name, actually.
148  	 */
149  	params->hdr.tag = ATAG_INITRD2;
150  	params->hdr.size = tag_size (tag_initrd);
151  
152  	params->u.initrd.start = initrd_start;
153  	params->u.initrd.size = initrd_end - initrd_start;
154  
155  	params = tag_next (params);
156  }
157  
158  static void setup_serial_tag(struct tag **tmp)
159  {
160  	struct tag *params = *tmp;
161  	struct tag_serialnr serialnr;
162  
163  	get_board_serial(&serialnr);
164  	params->hdr.tag = ATAG_SERIAL;
165  	params->hdr.size = tag_size (tag_serialnr);
166  	params->u.serialnr.low = serialnr.low;
167  	params->u.serialnr.high= serialnr.high;
168  	params = tag_next (params);
169  	*tmp = params;
170  }
171  
172  static void setup_revision_tag(struct tag **in_params)
173  {
174  	u32 rev = 0;
175  
176  	rev = get_board_rev();
177  	params->hdr.tag = ATAG_REVISION;
178  	params->hdr.size = tag_size (tag_revision);
179  	params->u.revision.rev = rev;
180  	params = tag_next (params);
181  }
182  
183  static void setup_end_tag(bd_t *bd)
184  {
185  	params->hdr.tag = ATAG_NONE;
186  	params->hdr.size = 0;
187  }
188  
189  __weak void setup_board_tags(struct tag **in_params) {}
190  
191  #ifdef CONFIG_ARM64
192  static void do_nonsec_virt_switch(void)
193  {
194  	smp_kick_all_cpus();
195  	dcache_disable();	/* flush cache before swtiching to EL2 */
196  	armv8_switch_to_el2();
197  #ifdef CONFIG_ARMV8_SWITCH_TO_EL1
198  	armv8_switch_to_el1();
199  #endif
200  }
201  #endif
202  
203  /* Subcommand: PREP */
204  static void boot_prep_linux(bootm_headers_t *images)
205  {
206  	char *commandline = getenv("bootargs");
207  
208  	if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) {
209  #ifdef CONFIG_OF_LIBFDT
210  		debug("using: FDT\n");
211  		if (image_setup_linux(images)) {
212  			printf("FDT creation failed! hanging...");
213  			hang();
214  		}
215  #endif
216  	} else if (BOOTM_ENABLE_TAGS) {
217  		debug("using: ATAGS\n");
218  		setup_start_tag(gd->bd);
219  		if (BOOTM_ENABLE_SERIAL_TAG)
220  			setup_serial_tag(&params);
221  		if (BOOTM_ENABLE_CMDLINE_TAG)
222  			setup_commandline_tag(gd->bd, commandline);
223  		if (BOOTM_ENABLE_REVISION_TAG)
224  			setup_revision_tag(&params);
225  		if (BOOTM_ENABLE_MEMORY_TAGS)
226  			setup_memory_tags(gd->bd);
227  		if (BOOTM_ENABLE_INITRD_TAG) {
228  			/*
229  			 * In boot_ramdisk_high(), it may relocate ramdisk to
230  			 * a specified location. And set images->initrd_start &
231  			 * images->initrd_end to relocated ramdisk's start/end
232  			 * addresses. So use them instead of images->rd_start &
233  			 * images->rd_end when possible.
234  			 */
235  			if (images->initrd_start && images->initrd_end) {
236  				setup_initrd_tag(gd->bd, images->initrd_start,
237  						 images->initrd_end);
238  			} else if (images->rd_start && images->rd_end) {
239  				setup_initrd_tag(gd->bd, images->rd_start,
240  						 images->rd_end);
241  			}
242  		}
243  		setup_board_tags(&params);
244  		setup_end_tag(gd->bd);
245  	} else {
246  		printf("FDT and ATAGS support not compiled in - hanging\n");
247  		hang();
248  	}
249  }
250  
251  #ifdef CONFIG_ARMV7_NONSEC
252  bool armv7_boot_nonsec(void)
253  {
254  	char *s = getenv("bootm_boot_mode");
255  #ifdef CONFIG_ARMV7_BOOT_SEC_DEFAULT
256  	bool nonsec = false;
257  #else
258  	bool nonsec = true;
259  #endif
260  
261  	if (s && !strcmp(s, "sec"))
262  		nonsec = false;
263  
264  	if (s && !strcmp(s, "nonsec"))
265  		nonsec = true;
266  
267  	return nonsec;
268  }
269  #endif
270  
271  /* Subcommand: GO */
272  static void boot_jump_linux(bootm_headers_t *images, int flag)
273  {
274  #ifdef CONFIG_ARM64
275  	void (*kernel_entry)(void *fdt_addr, void *res0, void *res1,
276  			void *res2);
277  	int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
278  
279  	kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1,
280  				void *res2))images->ep;
281  
282  	debug("## Transferring control to Linux (at address %lx)...\n",
283  		(ulong) kernel_entry);
284  	bootstage_mark(BOOTSTAGE_ID_RUN_OS);
285  
286  	announce_and_cleanup(fake);
287  
288  	if (!fake) {
289  		do_nonsec_virt_switch();
290  		kernel_entry(images->ft_addr, NULL, NULL, NULL);
291  	}
292  #else
293  	unsigned long machid = gd->bd->bi_arch_number;
294  	char *s;
295  	void (*kernel_entry)(int zero, int arch, uint params);
296  	unsigned long r2;
297  	int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
298  
299  	kernel_entry = (void (*)(int, int, uint))images->ep;
300  
301  	s = getenv("machid");
302  	if (s) {
303  		if (strict_strtoul(s, 16, &machid) < 0) {
304  			debug("strict_strtoul failed!\n");
305  			return;
306  		}
307  		printf("Using machid 0x%lx from environment\n", machid);
308  	}
309  
310  	debug("## Transferring control to Linux (at address %08lx)" \
311  		"...\n", (ulong) kernel_entry);
312  	bootstage_mark(BOOTSTAGE_ID_RUN_OS);
313  	announce_and_cleanup(fake);
314  
315  	if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
316  		r2 = (unsigned long)images->ft_addr;
317  	else
318  		r2 = gd->bd->bi_boot_params;
319  
320  	if (!fake) {
321  #ifdef CONFIG_ARMV7_NONSEC
322  		if (armv7_boot_nonsec()) {
323  			armv7_init_nonsec();
324  			secure_ram_addr(_do_nonsec_entry)(kernel_entry,
325  							  0, machid, r2);
326  		} else
327  #endif
328  			kernel_entry(0, machid, r2);
329  	}
330  #endif
331  }
332  
333  /* Main Entry point for arm bootm implementation
334   *
335   * Modeled after the powerpc implementation
336   * DIFFERENCE: Instead of calling prep and go at the end
337   * they are called if subcommand is equal 0.
338   */
339  int do_bootm_linux(int flag, int argc, char * const argv[],
340  		   bootm_headers_t *images)
341  {
342  	/* No need for those on ARM */
343  	if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
344  		return -1;
345  
346  	if (flag & BOOTM_STATE_OS_PREP) {
347  		boot_prep_linux(images);
348  		return 0;
349  	}
350  
351  	if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) {
352  		boot_jump_linux(images, flag);
353  		return 0;
354  	}
355  
356  	boot_prep_linux(images);
357  	boot_jump_linux(images, flag);
358  	return 0;
359  }
360  
361  #if defined(CONFIG_BOOTM_VXWORKS)
362  void boot_prep_vxworks(bootm_headers_t *images)
363  {
364  #if defined(CONFIG_OF_LIBFDT)
365  	int off;
366  
367  	if (images->ft_addr) {
368  		off = fdt_path_offset(images->ft_addr, "/memory");
369  		if (off < 0) {
370  			if (arch_fixup_fdt(images->ft_addr))
371  				puts("## WARNING: fixup memory failed!\n");
372  		}
373  	}
374  #endif
375  	cleanup_before_linux();
376  }
377  void boot_jump_vxworks(bootm_headers_t *images)
378  {
379  	/* ARM VxWorks requires device tree physical address to be passed */
380  	((void (*)(void *))images->ep)(images->ft_addr);
381  }
382  #endif
383