]>
Commit | Line | Data |
---|---|---|
7b64fef3 WD |
1 | /* |
2 | * Copyright (C) 2004-2006 Atmel Corporation | |
3 | * | |
4 | * See file CREDITS for list of people who contributed to this | |
5 | * project. | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License as | |
9 | * published by the Free Software Foundation; either version 2 of | |
10 | * the License, or (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
20 | * MA 02111-1307 USA | |
21 | */ | |
22 | #include <common.h> | |
23 | #include <command.h> | |
24 | #include <image.h> | |
25 | #include <zlib.h> | |
26 | #include <asm/byteorder.h> | |
27 | #include <asm/addrspace.h> | |
28 | #include <asm/io.h> | |
29 | #include <asm/setup.h> | |
df548d3c | 30 | #include <asm/arch/clk.h> |
7b64fef3 WD |
31 | |
32 | DECLARE_GLOBAL_DATA_PTR; | |
33 | ||
34 | extern int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]); | |
35 | ||
36 | /* CPU-specific hook to allow flushing of caches, etc. */ | |
37 | extern void prepare_to_boot(void); | |
38 | ||
7b64fef3 WD |
39 | extern image_header_t header; /* from cmd_bootm.c */ |
40 | ||
41 | static struct tag *setup_start_tag(struct tag *params) | |
42 | { | |
43 | params->hdr.tag = ATAG_CORE; | |
44 | params->hdr.size = tag_size(tag_core); | |
45 | ||
46 | params->u.core.flags = 0; | |
47 | params->u.core.pagesize = 4096; | |
48 | params->u.core.rootdev = 0; | |
49 | ||
50 | return tag_next(params); | |
51 | } | |
52 | ||
53 | static struct tag *setup_memory_tags(struct tag *params) | |
54 | { | |
55 | bd_t *bd = gd->bd; | |
56 | int i; | |
57 | ||
58 | for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { | |
59 | params->hdr.tag = ATAG_MEM; | |
60 | params->hdr.size = tag_size(tag_mem_range); | |
61 | ||
62 | params->u.mem_range.addr = bd->bi_dram[i].start; | |
63 | params->u.mem_range.size = bd->bi_dram[i].size; | |
64 | ||
65 | params = tag_next(params); | |
66 | } | |
67 | ||
68 | return params; | |
69 | } | |
70 | ||
71 | static struct tag *setup_commandline_tag(struct tag *params, char *cmdline) | |
72 | { | |
73 | if (!cmdline) | |
74 | return params; | |
75 | ||
76 | /* eat leading white space */ | |
77 | while (*cmdline == ' ') cmdline++; | |
78 | ||
79 | /* | |
80 | * Don't include tags for empty command lines; let the kernel | |
81 | * use its default command line. | |
82 | */ | |
83 | if (*cmdline == '\0') | |
84 | return params; | |
85 | ||
86 | params->hdr.tag = ATAG_CMDLINE; | |
87 | params->hdr.size = | |
88 | (sizeof (struct tag_header) + strlen(cmdline) + 1 + 3) >> 2; | |
89 | strcpy(params->u.cmdline.cmdline, cmdline); | |
90 | ||
91 | return tag_next(params); | |
92 | } | |
93 | ||
94 | static struct tag *setup_ramdisk_tag(struct tag *params, | |
95 | unsigned long rd_start, | |
96 | unsigned long rd_end) | |
97 | { | |
98 | if (rd_start == rd_end) | |
99 | return params; | |
100 | ||
101 | params->hdr.tag = ATAG_RDIMG; | |
102 | params->hdr.size = tag_size(tag_mem_range); | |
103 | ||
104 | params->u.mem_range.addr = rd_start; | |
105 | params->u.mem_range.size = rd_end - rd_start; | |
106 | ||
107 | return tag_next(params); | |
108 | } | |
109 | ||
110 | static struct tag *setup_clock_tags(struct tag *params) | |
111 | { | |
112 | params->hdr.tag = ATAG_CLOCK; | |
113 | params->hdr.size = tag_size(tag_clock); | |
114 | params->u.clock.clock_id = ACLOCK_BOOTCPU; | |
115 | params->u.clock.clock_flags = 0; | |
116 | params->u.clock.clock_hz = gd->cpu_hz; | |
117 | ||
118 | #ifdef CONFIG_AT32AP7000 | |
119 | /* | |
120 | * New kernels don't need this, but we should be backwards | |
121 | * compatible for a while... | |
122 | */ | |
123 | params = tag_next(params); | |
124 | ||
125 | params->hdr.tag = ATAG_CLOCK; | |
126 | params->hdr.size = tag_size(tag_clock); | |
127 | params->u.clock.clock_id = ACLOCK_HSB; | |
128 | params->u.clock.clock_flags = 0; | |
df548d3c | 129 | params->u.clock.clock_hz = get_hsb_clk_rate(); |
7b64fef3 WD |
130 | #endif |
131 | ||
132 | return tag_next(params); | |
133 | } | |
134 | ||
135 | static struct tag *setup_ethernet_tag(struct tag *params, | |
136 | char *addr, int index) | |
137 | { | |
138 | char *s, *e; | |
139 | int i; | |
140 | ||
141 | params->hdr.tag = ATAG_ETHERNET; | |
142 | params->hdr.size = tag_size(tag_ethernet); | |
143 | ||
144 | params->u.ethernet.mac_index = index; | |
145 | params->u.ethernet.mii_phy_addr = gd->bd->bi_phy_id[index]; | |
146 | ||
147 | s = addr; | |
148 | for (i = 0; i < 6; i++) { | |
149 | params->u.ethernet.hw_address[i] = simple_strtoul(s, &e, 16); | |
150 | s = e + 1; | |
151 | } | |
152 | ||
153 | return tag_next(params); | |
154 | } | |
155 | ||
156 | static struct tag *setup_ethernet_tags(struct tag *params) | |
157 | { | |
158 | char name[16] = "ethaddr"; | |
159 | char *addr; | |
160 | int i = 0; | |
161 | ||
162 | do { | |
163 | addr = getenv(name); | |
164 | if (addr) | |
165 | params = setup_ethernet_tag(params, addr, i); | |
166 | sprintf(name, "eth%daddr", ++i); | |
167 | } while (i < 4); | |
168 | ||
169 | return params; | |
170 | } | |
171 | ||
172 | static void setup_end_tag(struct tag *params) | |
173 | { | |
174 | params->hdr.tag = ATAG_NONE; | |
175 | params->hdr.size = 0; | |
176 | } | |
177 | ||
178 | void do_bootm_linux(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[], | |
179 | unsigned long addr, unsigned long *len_ptr, int verify) | |
180 | { | |
181 | unsigned long data, len = 0; | |
182 | unsigned long initrd_start, initrd_end; | |
183 | unsigned long image_start, image_end; | |
184 | unsigned long checksum; | |
185 | void (*theKernel)(int magic, void *tagtable); | |
186 | image_header_t *hdr; | |
187 | struct tag *params, *params_start; | |
188 | char *commandline = getenv("bootargs"); | |
189 | ||
190 | hdr = (image_header_t *)addr; | |
191 | image_start = addr; | |
192 | image_end = addr + hdr->ih_size; | |
193 | ||
194 | theKernel = (void *)ntohl(hdr->ih_ep); | |
195 | ||
196 | /* | |
197 | * Check if there is an initrd image | |
198 | */ | |
199 | if (argc >= 3) { | |
fad63407 | 200 | show_boot_progress (9); |
7b64fef3 WD |
201 | |
202 | addr = simple_strtoul(argv[2], NULL, 16); | |
203 | ||
204 | printf("## Loading RAMDISK image at %08lx ...\n", addr); | |
205 | ||
206 | memcpy(&header, (char *)addr, sizeof(header)); | |
207 | hdr = &header; | |
208 | ||
209 | if (ntohl(hdr->ih_magic) != IH_MAGIC) { | |
210 | puts("Bad Magic Number\n"); | |
fad63407 | 211 | show_boot_progress (-10); |
7b64fef3 WD |
212 | do_reset(cmdtp, flag, argc, argv); |
213 | } | |
214 | ||
215 | data = (unsigned long)hdr; | |
216 | len = sizeof(*hdr); | |
217 | checksum = ntohl(hdr->ih_hcrc); | |
218 | hdr->ih_hcrc = 0; | |
219 | ||
220 | if (crc32(0, (unsigned char *)data, len) != checksum) { | |
221 | puts("Bad Header Checksum\n"); | |
fad63407 | 222 | show_boot_progress (-11); |
7b64fef3 WD |
223 | do_reset(cmdtp, flag, argc, argv); |
224 | } | |
225 | ||
fad63407 | 226 | show_boot_progress (10); |
7b64fef3 WD |
227 | |
228 | print_image_hdr(hdr); | |
229 | ||
230 | data = addr + sizeof(header); | |
231 | len = ntohl(hdr->ih_size); | |
232 | ||
233 | if (verify) { | |
234 | unsigned long csum = 0; | |
235 | ||
236 | puts(" Verifying Checksum ... "); | |
237 | csum = crc32(0, (unsigned char *)data, len); | |
238 | if (csum != ntohl(hdr->ih_dcrc)) { | |
239 | puts("Bad Data CRC\n"); | |
fad63407 | 240 | show_boot_progress (-12); |
7b64fef3 WD |
241 | do_reset(cmdtp, flag, argc, argv); |
242 | } | |
243 | puts("OK\n"); | |
244 | } | |
245 | ||
fad63407 | 246 | show_boot_progress (11); |
7b64fef3 WD |
247 | |
248 | if ((hdr->ih_os != IH_OS_LINUX) || | |
249 | (hdr->ih_arch != IH_CPU_AVR32) || | |
250 | (hdr->ih_type != IH_TYPE_RAMDISK)) { | |
251 | puts("Not a Linux/AVR32 RAMDISK image\n"); | |
fad63407 | 252 | show_boot_progress (-13); |
7b64fef3 WD |
253 | do_reset(cmdtp, flag, argc, argv); |
254 | } | |
255 | } else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) { | |
256 | ulong tail = ntohl (len_ptr[0]) % 4; | |
257 | int i; | |
258 | ||
fad63407 | 259 | show_boot_progress (13); |
7b64fef3 WD |
260 | |
261 | /* skip kernel length and terminator */ | |
262 | data = (ulong) (&len_ptr[2]); | |
263 | /* skip any additional image length fields */ | |
264 | for (i = 1; len_ptr[i]; ++i) | |
265 | data += 4; | |
266 | /* add kernel length, and align */ | |
267 | data += ntohl (len_ptr[0]); | |
268 | if (tail) { | |
269 | data += 4 - tail; | |
270 | } | |
271 | ||
272 | len = ntohl (len_ptr[1]); | |
273 | } else { | |
274 | /* no initrd image */ | |
fad63407 | 275 | show_boot_progress (14); |
7b64fef3 WD |
276 | len = data = 0; |
277 | } | |
278 | ||
279 | if (data) { | |
280 | initrd_start = data; | |
281 | initrd_end = initrd_start + len; | |
282 | } else { | |
283 | initrd_start = 0; | |
284 | initrd_end = 0; | |
285 | } | |
286 | ||
fad63407 | 287 | show_boot_progress (15); |
7b64fef3 WD |
288 | |
289 | params = params_start = (struct tag *)gd->bd->bi_boot_params; | |
290 | params = setup_start_tag(params); | |
291 | params = setup_memory_tags(params); | |
292 | if (initrd_start) { | |
293 | params = setup_ramdisk_tag(params, | |
294 | PHYSADDR(initrd_start), | |
295 | PHYSADDR(initrd_end)); | |
296 | } | |
297 | params = setup_commandline_tag(params, commandline); | |
298 | params = setup_clock_tags(params); | |
299 | params = setup_ethernet_tags(params); | |
300 | setup_end_tag(params); | |
301 | ||
302 | printf("\nStarting kernel at %p (params at %p)...\n\n", | |
303 | theKernel, params_start); | |
304 | ||
305 | prepare_to_boot(); | |
306 | ||
307 | theKernel(ATAG_MAGIC, params_start); | |
308 | } |