Fix loading kernel from official vendor firmware
[oweals/u-boot_mod.git] / u-boot / lib_mips / mips_linux.c
1 /*
2  * (C) Copyright 2003
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include <common.h>
25 #include <command.h>
26 #include <image.h>
27 #include <asm/byteorder.h>
28 #include <asm/addrspace.h>
29 #include <ar7240_soc.h>
30
31 /* #define DEBUG */
32
33 DECLARE_GLOBAL_DATA_PTR;
34
35 #define LINUX_MAX_ENVS  512
36 #define LINUX_MAX_ARGS  512
37
38 /* From cmd_bootm.c */
39 extern image_header_t header;
40
41 static int linux_argc;
42 static int linux_env_idx;
43
44 static char **linux_argv;
45 static char **linux_env;
46 static char  *linux_env_p;
47
48 #if defined(CONFIG_WASP_SUPPORT)
49 void wasp_set_cca(void)
50 {
51         /* Set cache coherency attribute */
52         asm("mfc0 $t0, $16\n"   /* CP0_CONFIG == 16 */
53             "li   $t1, ~7\n"
54             "and  $t0, $t0, $t1\n"
55             "ori  $t0, 3\n"     /* CONF_CM_CACHABLE_NONCOHERENT */
56             "mtc0 $t0, $16\n"   /* CP0_CONFIG == 16 */
57             "nop\n": : );
58 }
59 #endif
60
61 static void linux_params_init(ulong start, char *cmdline)
62 {
63         char memstr[16];
64         char *next, *quote, *argp;
65
66         linux_argc = 1;
67         linux_argv = (char **)start;
68         linux_argv[0] = 0;
69         argp = (char *)(linux_argv + LINUX_MAX_ARGS);
70
71         next = cmdline;
72
73         if (strstr(cmdline, "mem="))
74                 memstr[0] = 0;
75         else
76                 memstr[0] = 1;
77
78         while (cmdline && *cmdline && linux_argc < LINUX_MAX_ARGS) {
79                 quote = strchr(cmdline, '"');
80                 next  = strchr(cmdline, ' ');
81
82                 while (next != NULL && quote != NULL && quote < next) {
83                         /*
84                          * We found a left quote before the next blank
85                          * now we have to find the matching right quote
86                          */
87                         next = strchr(quote + 1, '"');
88                         if (next != NULL) {
89                                 quote = strchr(next + 1, '"');
90                                 next  = strchr(next + 1, ' ');
91                         }
92                 }
93
94                 if (next == NULL)
95                         next = cmdline + strlen(cmdline);
96
97                 linux_argv[linux_argc] = argp;
98                 memcpy(argp, cmdline, next - cmdline);
99                 argp[next - cmdline] = 0;
100
101                 argp += next - cmdline + 1;
102                 linux_argc++;
103
104                 if (*next)
105                         next++;
106
107                 cmdline = next;
108         }
109
110         /* Add mem size to command line if it's missing' */
111         if (memstr[0]) {
112                 sprintf(memstr, "mem=%luM", gd->ram_size >> 20);
113                 memcpy(argp, memstr, strlen(memstr) + 1);
114
115                 linux_argv[linux_argc] = argp;
116                 linux_argc++;
117
118                 argp += strlen(memstr) + 1;
119         }
120
121         linux_env = (char **)(((ulong)argp + 15) & ~15);
122         linux_env[0] = 0;
123
124         linux_env_p = (char *)(linux_env + LINUX_MAX_ENVS);
125         linux_env_idx = 0;
126 }
127
128 static void linux_env_set(char *env_name, char *env_val)
129 {
130         if (linux_env_idx < LINUX_MAX_ENVS - 1) {
131                 linux_env[linux_env_idx] = linux_env_p;
132
133                 strcpy(linux_env_p, env_name);
134                 linux_env_p += strlen(env_name);
135
136                 strcpy(linux_env_p, "=");
137                 linux_env_p += 1;
138
139                 strcpy(linux_env_p, env_val);
140                 linux_env_p += strlen(env_val);
141
142                 linux_env_p++;
143                 linux_env[++linux_env_idx] = 0;
144         }
145 }
146
147 void do_bootm_linux(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
148 {
149         int lsdk_kernel;
150         char *cmdline, *s, buf[16];
151         image_header_t *hdr = &header;
152         void(*Kernel)(int, char **, char **);
153         void(*LSDKKernel)(int, char **, ulong, ulong);
154
155         cmdline = getenv("bootargs");
156
157 #if defined(DEBUG)
158         printf("## Bootargs: '%s'\n", cmdline);
159         printf("## Transferring control to Linux (at address 0x%08lx) ...\n",
160                (ulong)(ntohl(hdr->ih_ep)));
161 #endif
162
163         linux_params_init(UNCACHED_SDRAM(gd->bd->bi_boot_params), cmdline);
164
165 #if defined(DEBUG)
166         printf("## Giving linux memsize in MB, %lu\n", gd->ram_size >> 20);
167 #endif
168
169         sprintf(buf, "%lu", gd->ram_size >> 20);
170         linux_env_set("memsize", buf);
171
172         sprintf(buf, "0x%X", (unsigned int)(gd->bd->bi_flashstart));
173         linux_env_set("flash_start", buf);
174
175         sprintf(buf, "0x%X", (unsigned int)(gd->bd->bi_flashsize));
176         linux_env_set("flash_size", buf);
177
178         /* We assume that the kernel is in place */
179         printf("Starting kernel...\n\n");
180
181 #if defined(CONFIG_WASP_SUPPORT)
182         wasp_set_cca();
183 #endif
184
185 #if defined(CONFIG_FOR_DRAGINO_V2) || defined(CONFIG_FOR_MESH_POTATO_V2)
186         /* Restore WAN/LAN LEDs (BIT 3 and 7 in GPIO_FUNCTION_1) */
187         ar7240_reg_wr(AR7240_GPIO_FUNC,
188                       (ar7240_reg_rd(AR7240_GPIO_FUNC) | 0x88));
189 #endif
190
191         /*
192          * In some of old Atheros LSDK based firmware versions (kernel 2.6.x),
193          * kernel expects RAM size (in bytes) in 3rd parameter and FLASH
194          * size (in MB) in 4th. Also, most of old TP-Link firmwares follow
195          * this 'broken' approach.
196          *
197          * To make it somehow universal, we will use here a dedicated env
198          * flag 'lsdk_kernel' - if it's set to > 0, RAM and FLASH sizes
199          * will be passed as 3rd and 4th parameters.
200          */
201         s = getenv("lsdk_kernel");
202         lsdk_kernel = s ? simple_strtol(s, NULL, 10) : 0;
203
204         if (lsdk_kernel > 0) {
205                 LSDKKernel = (void (*)(int, char **,
206                                        ulong, ulong))ntohl(hdr->ih_ep);
207
208                 LSDKKernel(linux_argc, linux_argv, gd->ram_size,
209                            gd->bd->bi_flashsize >> 20);
210         } else {
211                 Kernel = (void (*)(int, char **, char **))ntohl(hdr->ih_ep);
212
213                 Kernel(linux_argc, linux_argv, linux_env);
214         }
215 }