1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * (C) Copyright 2003
3*4882a593Smuzhiyun * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0+
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <common.h>
9*4882a593Smuzhiyun #include <image.h>
10*4882a593Smuzhiyun #include <fdt_support.h>
11*4882a593Smuzhiyun #include <asm/addrspace.h>
12*4882a593Smuzhiyun #include <asm/io.h>
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun DECLARE_GLOBAL_DATA_PTR;
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #define LINUX_MAX_ENVS 256
17*4882a593Smuzhiyun #define LINUX_MAX_ARGS 256
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun static int linux_argc;
20*4882a593Smuzhiyun static char **linux_argv;
21*4882a593Smuzhiyun static char *linux_argp;
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun static char **linux_env;
24*4882a593Smuzhiyun static char *linux_env_p;
25*4882a593Smuzhiyun static int linux_env_idx;
26*4882a593Smuzhiyun
arch_get_sp(void)27*4882a593Smuzhiyun static ulong arch_get_sp(void)
28*4882a593Smuzhiyun {
29*4882a593Smuzhiyun ulong ret;
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun __asm__ __volatile__("move %0, $sp" : "=r"(ret) : );
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun return ret;
34*4882a593Smuzhiyun }
35*4882a593Smuzhiyun
arch_lmb_reserve(struct lmb * lmb)36*4882a593Smuzhiyun void arch_lmb_reserve(struct lmb *lmb)
37*4882a593Smuzhiyun {
38*4882a593Smuzhiyun ulong sp;
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun sp = arch_get_sp();
41*4882a593Smuzhiyun debug("## Current stack ends at 0x%08lx\n", sp);
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun /* adjust sp by 4K to be safe */
44*4882a593Smuzhiyun sp -= 4096;
45*4882a593Smuzhiyun lmb_reserve(lmb, sp, gd->ram_top - sp);
46*4882a593Smuzhiyun }
47*4882a593Smuzhiyun
linux_cmdline_init(void)48*4882a593Smuzhiyun static void linux_cmdline_init(void)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun linux_argc = 1;
51*4882a593Smuzhiyun linux_argv = (char **)UNCACHED_SDRAM(gd->bd->bi_boot_params);
52*4882a593Smuzhiyun linux_argv[0] = 0;
53*4882a593Smuzhiyun linux_argp = (char *)(linux_argv + LINUX_MAX_ARGS);
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun
linux_cmdline_set(const char * value,size_t len)56*4882a593Smuzhiyun static void linux_cmdline_set(const char *value, size_t len)
57*4882a593Smuzhiyun {
58*4882a593Smuzhiyun linux_argv[linux_argc] = linux_argp;
59*4882a593Smuzhiyun memcpy(linux_argp, value, len);
60*4882a593Smuzhiyun linux_argp[len] = 0;
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun linux_argp += len + 1;
63*4882a593Smuzhiyun linux_argc++;
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
linux_cmdline_dump(void)66*4882a593Smuzhiyun static void linux_cmdline_dump(void)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun int i;
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun debug("## cmdline argv at 0x%p, argp at 0x%p\n",
71*4882a593Smuzhiyun linux_argv, linux_argp);
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun for (i = 1; i < linux_argc; i++)
74*4882a593Smuzhiyun debug(" arg %03d: %s\n", i, linux_argv[i]);
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun
linux_cmdline_legacy(bootm_headers_t * images)77*4882a593Smuzhiyun static void linux_cmdline_legacy(bootm_headers_t *images)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun const char *bootargs, *next, *quote;
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun linux_cmdline_init();
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun bootargs = env_get("bootargs");
84*4882a593Smuzhiyun if (!bootargs)
85*4882a593Smuzhiyun return;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun next = bootargs;
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun while (bootargs && *bootargs && linux_argc < LINUX_MAX_ARGS) {
90*4882a593Smuzhiyun quote = strchr(bootargs, '"');
91*4882a593Smuzhiyun next = strchr(bootargs, ' ');
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun while (next && quote && quote < next) {
94*4882a593Smuzhiyun /*
95*4882a593Smuzhiyun * we found a left quote before the next blank
96*4882a593Smuzhiyun * now we have to find the matching right quote
97*4882a593Smuzhiyun */
98*4882a593Smuzhiyun next = strchr(quote + 1, '"');
99*4882a593Smuzhiyun if (next) {
100*4882a593Smuzhiyun quote = strchr(next + 1, '"');
101*4882a593Smuzhiyun next = strchr(next + 1, ' ');
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun if (!next)
106*4882a593Smuzhiyun next = bootargs + strlen(bootargs);
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun linux_cmdline_set(bootargs, next - bootargs);
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun if (*next)
111*4882a593Smuzhiyun next++;
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun bootargs = next;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun
linux_cmdline_append(bootm_headers_t * images)117*4882a593Smuzhiyun static void linux_cmdline_append(bootm_headers_t *images)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun char buf[24];
120*4882a593Smuzhiyun ulong mem, rd_start, rd_size;
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun /* append mem */
123*4882a593Smuzhiyun mem = gd->ram_size >> 20;
124*4882a593Smuzhiyun sprintf(buf, "mem=%luM", mem);
125*4882a593Smuzhiyun linux_cmdline_set(buf, strlen(buf));
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun /* append rd_start and rd_size */
128*4882a593Smuzhiyun rd_start = images->initrd_start;
129*4882a593Smuzhiyun rd_size = images->initrd_end - images->initrd_start;
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun if (rd_size) {
132*4882a593Smuzhiyun sprintf(buf, "rd_start=0x%08lX", rd_start);
133*4882a593Smuzhiyun linux_cmdline_set(buf, strlen(buf));
134*4882a593Smuzhiyun sprintf(buf, "rd_size=0x%lX", rd_size);
135*4882a593Smuzhiyun linux_cmdline_set(buf, strlen(buf));
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun
linux_env_init(void)139*4882a593Smuzhiyun static void linux_env_init(void)
140*4882a593Smuzhiyun {
141*4882a593Smuzhiyun linux_env = (char **)(((ulong) linux_argp + 15) & ~15);
142*4882a593Smuzhiyun linux_env[0] = 0;
143*4882a593Smuzhiyun linux_env_p = (char *)(linux_env + LINUX_MAX_ENVS);
144*4882a593Smuzhiyun linux_env_idx = 0;
145*4882a593Smuzhiyun }
146*4882a593Smuzhiyun
linux_env_set(const char * env_name,const char * env_val)147*4882a593Smuzhiyun static void linux_env_set(const char *env_name, const char *env_val)
148*4882a593Smuzhiyun {
149*4882a593Smuzhiyun if (linux_env_idx < LINUX_MAX_ENVS - 1) {
150*4882a593Smuzhiyun linux_env[linux_env_idx] = linux_env_p;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun strcpy(linux_env_p, env_name);
153*4882a593Smuzhiyun linux_env_p += strlen(env_name);
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun if (CONFIG_IS_ENABLED(MALTA)) {
156*4882a593Smuzhiyun linux_env_p++;
157*4882a593Smuzhiyun linux_env[++linux_env_idx] = linux_env_p;
158*4882a593Smuzhiyun } else {
159*4882a593Smuzhiyun *linux_env_p++ = '=';
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun strcpy(linux_env_p, env_val);
163*4882a593Smuzhiyun linux_env_p += strlen(env_val);
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun linux_env_p++;
166*4882a593Smuzhiyun linux_env[++linux_env_idx] = 0;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun
linux_env_legacy(bootm_headers_t * images)170*4882a593Smuzhiyun static void linux_env_legacy(bootm_headers_t *images)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun char env_buf[12];
173*4882a593Smuzhiyun const char *cp;
174*4882a593Smuzhiyun ulong rd_start, rd_size;
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun if (CONFIG_IS_ENABLED(MEMSIZE_IN_BYTES)) {
177*4882a593Smuzhiyun sprintf(env_buf, "%lu", (ulong)gd->ram_size);
178*4882a593Smuzhiyun debug("## Giving linux memsize in bytes, %lu\n",
179*4882a593Smuzhiyun (ulong)gd->ram_size);
180*4882a593Smuzhiyun } else {
181*4882a593Smuzhiyun sprintf(env_buf, "%lu", (ulong)(gd->ram_size >> 20));
182*4882a593Smuzhiyun debug("## Giving linux memsize in MB, %lu\n",
183*4882a593Smuzhiyun (ulong)(gd->ram_size >> 20));
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun rd_start = UNCACHED_SDRAM(images->initrd_start);
187*4882a593Smuzhiyun rd_size = images->initrd_end - images->initrd_start;
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun linux_env_init();
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun linux_env_set("memsize", env_buf);
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun sprintf(env_buf, "0x%08lX", rd_start);
194*4882a593Smuzhiyun linux_env_set("initrd_start", env_buf);
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun sprintf(env_buf, "0x%lX", rd_size);
197*4882a593Smuzhiyun linux_env_set("initrd_size", env_buf);
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun sprintf(env_buf, "0x%08X", (uint) (gd->bd->bi_flashstart));
200*4882a593Smuzhiyun linux_env_set("flash_start", env_buf);
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun sprintf(env_buf, "0x%X", (uint) (gd->bd->bi_flashsize));
203*4882a593Smuzhiyun linux_env_set("flash_size", env_buf);
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun cp = env_get("ethaddr");
206*4882a593Smuzhiyun if (cp)
207*4882a593Smuzhiyun linux_env_set("ethaddr", cp);
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun cp = env_get("eth1addr");
210*4882a593Smuzhiyun if (cp)
211*4882a593Smuzhiyun linux_env_set("eth1addr", cp);
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun if (CONFIG_IS_ENABLED(MALTA)) {
214*4882a593Smuzhiyun sprintf(env_buf, "%un8r", gd->baudrate);
215*4882a593Smuzhiyun linux_env_set("modetty0", env_buf);
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun
boot_reloc_ramdisk(bootm_headers_t * images)219*4882a593Smuzhiyun static int boot_reloc_ramdisk(bootm_headers_t *images)
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun ulong rd_len = images->rd_end - images->rd_start;
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun /*
224*4882a593Smuzhiyun * In case of legacy uImage's, relocation of ramdisk is already done
225*4882a593Smuzhiyun * by do_bootm_states() and should not repeated in 'bootm prep'.
226*4882a593Smuzhiyun */
227*4882a593Smuzhiyun if (images->state & BOOTM_STATE_RAMDISK) {
228*4882a593Smuzhiyun debug("## Ramdisk already relocated\n");
229*4882a593Smuzhiyun return 0;
230*4882a593Smuzhiyun }
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun return boot_ramdisk_high(&images->lmb, images->rd_start,
233*4882a593Smuzhiyun rd_len, &images->initrd_start, &images->initrd_end);
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun
boot_reloc_fdt(bootm_headers_t * images)236*4882a593Smuzhiyun static int boot_reloc_fdt(bootm_headers_t *images)
237*4882a593Smuzhiyun {
238*4882a593Smuzhiyun /*
239*4882a593Smuzhiyun * In case of legacy uImage's, relocation of FDT is already done
240*4882a593Smuzhiyun * by do_bootm_states() and should not repeated in 'bootm prep'.
241*4882a593Smuzhiyun */
242*4882a593Smuzhiyun if (images->state & BOOTM_STATE_FDT) {
243*4882a593Smuzhiyun debug("## FDT already relocated\n");
244*4882a593Smuzhiyun return 0;
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun #if CONFIG_IS_ENABLED(MIPS_BOOT_FDT) && CONFIG_IS_ENABLED(OF_LIBFDT)
248*4882a593Smuzhiyun boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr);
249*4882a593Smuzhiyun return boot_relocate_fdt(&images->lmb, &images->ft_addr,
250*4882a593Smuzhiyun &images->ft_len);
251*4882a593Smuzhiyun #else
252*4882a593Smuzhiyun return 0;
253*4882a593Smuzhiyun #endif
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun #if CONFIG_IS_ENABLED(MIPS_BOOT_FDT) && CONFIG_IS_ENABLED(OF_LIBFDT)
arch_fixup_fdt(void * blob)257*4882a593Smuzhiyun int arch_fixup_fdt(void *blob)
258*4882a593Smuzhiyun {
259*4882a593Smuzhiyun u64 mem_start = virt_to_phys((void *)gd->bd->bi_memstart);
260*4882a593Smuzhiyun u64 mem_size = gd->ram_size;
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun return fdt_fixup_memory_banks(blob, &mem_start, &mem_size, 1);
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun #endif
265*4882a593Smuzhiyun
boot_setup_fdt(bootm_headers_t * images)266*4882a593Smuzhiyun static int boot_setup_fdt(bootm_headers_t *images)
267*4882a593Smuzhiyun {
268*4882a593Smuzhiyun return image_setup_libfdt(images, images->ft_addr, images->ft_len,
269*4882a593Smuzhiyun &images->lmb);
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun
boot_prep_linux(bootm_headers_t * images)272*4882a593Smuzhiyun static void boot_prep_linux(bootm_headers_t *images)
273*4882a593Smuzhiyun {
274*4882a593Smuzhiyun boot_reloc_ramdisk(images);
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun if (CONFIG_IS_ENABLED(MIPS_BOOT_FDT) && images->ft_len) {
277*4882a593Smuzhiyun boot_reloc_fdt(images);
278*4882a593Smuzhiyun boot_setup_fdt(images);
279*4882a593Smuzhiyun } else {
280*4882a593Smuzhiyun if (CONFIG_IS_ENABLED(MIPS_BOOT_CMDLINE_LEGACY)) {
281*4882a593Smuzhiyun linux_cmdline_legacy(images);
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun if (!CONFIG_IS_ENABLED(MIPS_BOOT_ENV_LEGACY))
284*4882a593Smuzhiyun linux_cmdline_append(images);
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun linux_cmdline_dump();
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun if (CONFIG_IS_ENABLED(MIPS_BOOT_ENV_LEGACY))
290*4882a593Smuzhiyun linux_env_legacy(images);
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun
boot_jump_linux(bootm_headers_t * images)294*4882a593Smuzhiyun static void boot_jump_linux(bootm_headers_t *images)
295*4882a593Smuzhiyun {
296*4882a593Smuzhiyun typedef void __noreturn (*kernel_entry_t)(int, ulong, ulong, ulong);
297*4882a593Smuzhiyun kernel_entry_t kernel = (kernel_entry_t) images->ep;
298*4882a593Smuzhiyun ulong linux_extra = 0;
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun debug("## Transferring control to Linux (at address %p) ...\n", kernel);
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun bootstage_mark(BOOTSTAGE_ID_RUN_OS);
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun if (CONFIG_IS_ENABLED(MALTA))
305*4882a593Smuzhiyun linux_extra = gd->ram_size;
306*4882a593Smuzhiyun
307*4882a593Smuzhiyun #if CONFIG_IS_ENABLED(BOOTSTAGE_FDT)
308*4882a593Smuzhiyun bootstage_fdt_add_report();
309*4882a593Smuzhiyun #endif
310*4882a593Smuzhiyun #if CONFIG_IS_ENABLED(BOOTSTAGE_REPORT)
311*4882a593Smuzhiyun bootstage_report();
312*4882a593Smuzhiyun #endif
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun if (images->ft_len)
315*4882a593Smuzhiyun kernel(-2, (ulong)images->ft_addr, 0, 0);
316*4882a593Smuzhiyun else
317*4882a593Smuzhiyun kernel(linux_argc, (ulong)linux_argv, (ulong)linux_env,
318*4882a593Smuzhiyun linux_extra);
319*4882a593Smuzhiyun }
320*4882a593Smuzhiyun
do_bootm_linux(int flag,int argc,char * const argv[],bootm_headers_t * images)321*4882a593Smuzhiyun int do_bootm_linux(int flag, int argc, char * const argv[],
322*4882a593Smuzhiyun bootm_headers_t *images)
323*4882a593Smuzhiyun {
324*4882a593Smuzhiyun /* No need for those on MIPS */
325*4882a593Smuzhiyun if (flag & BOOTM_STATE_OS_BD_T)
326*4882a593Smuzhiyun return -1;
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun /*
329*4882a593Smuzhiyun * Cmdline init has been moved to 'bootm prep' because it has to be
330*4882a593Smuzhiyun * done after relocation of ramdisk to always pass correct values
331*4882a593Smuzhiyun * for rd_start and rd_size to Linux kernel.
332*4882a593Smuzhiyun */
333*4882a593Smuzhiyun if (flag & BOOTM_STATE_OS_CMDLINE)
334*4882a593Smuzhiyun return 0;
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun if (flag & BOOTM_STATE_OS_PREP) {
337*4882a593Smuzhiyun boot_prep_linux(images);
338*4882a593Smuzhiyun return 0;
339*4882a593Smuzhiyun }
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) {
342*4882a593Smuzhiyun boot_jump_linux(images);
343*4882a593Smuzhiyun return 0;
344*4882a593Smuzhiyun }
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun /* does not return */
347*4882a593Smuzhiyun return 1;
348*4882a593Smuzhiyun }
349