1 | /* |
---|
2 | * 2nd - OMAP "second stage" tt-loader |
---|
3 | * |
---|
4 | * Copyright (C) 2008 Guillaume Bougard <gbougard@pkg.fr> |
---|
5 | * Copyright (C) 2005 Luis Recuerda <lrec@helios.homeip.net> |
---|
6 | * |
---|
7 | * ARM kernel loader. Adapt from qemu-neo1973 |
---|
8 | * Copyright (c) 2006-2007 CodeSourcery. |
---|
9 | * Written by Paul Brook |
---|
10 | * |
---|
11 | * This program is free software; you can redistribute it and/or |
---|
12 | * modify it under the terms of the GNU General Public License |
---|
13 | * as published by the Free Software Foundation; either version 2 |
---|
14 | * of the License, or (at your option) any later version. |
---|
15 | * |
---|
16 | * This program is distributed in the hope that it will be useful, |
---|
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
19 | * GNU General Public License for more details. |
---|
20 | * |
---|
21 | * You should have received a copy of the GNU General Public License |
---|
22 | * along with this program; if not, write to the Free Software |
---|
23 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
---|
24 | * |
---|
25 | */ |
---|
26 | |
---|
27 | #include "config.h" |
---|
28 | #include "usb.h" |
---|
29 | |
---|
30 | extern u32 crc32( u32 crc, char* buf, u32 len, crc_cb_fnc_t* cb); |
---|
31 | |
---|
32 | /* |
---|
33 | * Length of memory dump done by 2nd.bin, must be a multiple of DUMP_LINE_SIZE from host main.c |
---|
34 | * Must also be sufficient to output lines of text, let's say 128 bytes |
---|
35 | */ |
---|
36 | #define MEM_READ_SIZE 128*32 |
---|
37 | #define CHUNK_SIZE 8192 |
---|
38 | |
---|
39 | static char *usb_hdr = "TIS" ; |
---|
40 | static char usb_outbuffer[MEM_READ_SIZE+4]; |
---|
41 | |
---|
42 | static u32 strcpy ( char *dest, char *src ) |
---|
43 | { |
---|
44 | u32 count = 0 ; |
---|
45 | char *tmp = dest, *s = src; |
---|
46 | |
---|
47 | while ( ++count < MEM_READ_SIZE && *s != '\0' ) |
---|
48 | *tmp++ = *s++ ; |
---|
49 | |
---|
50 | *tmp = '\0' ; |
---|
51 | |
---|
52 | return count ; |
---|
53 | } |
---|
54 | |
---|
55 | static u32 strlen ( char *src ) |
---|
56 | { |
---|
57 | u32 len = 0 ; |
---|
58 | |
---|
59 | while ( *src != '\0' ) { |
---|
60 | src++ ; |
---|
61 | len ++ ; |
---|
62 | } |
---|
63 | |
---|
64 | return len ; |
---|
65 | } |
---|
66 | |
---|
67 | static u32 memcpy ( char *dest, char *src, u32 count ) |
---|
68 | { |
---|
69 | char *tmp = dest, *s = src; |
---|
70 | u32 step = count ; |
---|
71 | |
---|
72 | while (step--) |
---|
73 | *tmp++ = *s++; |
---|
74 | |
---|
75 | return count ; |
---|
76 | } |
---|
77 | |
---|
78 | static void usb_msg (const char cmd, const char *msg) |
---|
79 | { |
---|
80 | u32 len = strcpy(usb_outbuffer,usb_hdr); |
---|
81 | usb_outbuffer[len-1] = cmd ; |
---|
82 | |
---|
83 | len += strcpy(&usb_outbuffer[len], msg ? (char *)msg : "" ); |
---|
84 | |
---|
85 | usb_send ( usb_outbuffer, len); |
---|
86 | while (!usb_sent ()); |
---|
87 | } |
---|
88 | |
---|
89 | static u32 usb_code (char cmd, u32 code) |
---|
90 | { |
---|
91 | u32 len = strcpy(usb_outbuffer,usb_hdr); |
---|
92 | usb_outbuffer[len-1] = cmd ; |
---|
93 | (*(u32 *)(usb_outbuffer+len)) = code ; |
---|
94 | len += sizeof(u32); |
---|
95 | |
---|
96 | usb_send ( usb_outbuffer, len ); |
---|
97 | while (!usb_sent ()); |
---|
98 | return code ; |
---|
99 | } |
---|
100 | |
---|
101 | static void usb_blk (const char cmd, char *mem, u32 size) |
---|
102 | { |
---|
103 | u32 len = strcpy(usb_outbuffer,usb_hdr); |
---|
104 | usb_outbuffer[len-1] = cmd ; |
---|
105 | |
---|
106 | len += memcpy(usb_outbuffer+len,mem,size); |
---|
107 | |
---|
108 | usb_send ( usb_outbuffer, len ); |
---|
109 | while (!usb_sent ()); |
---|
110 | } |
---|
111 | |
---|
112 | // Code from linux-2.6.24.3/arch/arm/mach-omap1/id.c source |
---|
113 | static u16 omap_get_jtag_id(void) |
---|
114 | { |
---|
115 | u32 prod_id, jtag_id; |
---|
116 | |
---|
117 | prod_id = omap_readl(OMAP_PRODUCTION_ID_1); |
---|
118 | jtag_id = omap_readl(OMAP32_ID_1); |
---|
119 | |
---|
120 | /* Check for unusable OMAP_PRODUCTION_ID_1 on 1611B/5912 and 730 */ |
---|
121 | if (((prod_id >> 20) == 0) || (prod_id == jtag_id)) |
---|
122 | prod_id = 0; |
---|
123 | else |
---|
124 | prod_id &= 0xffff; |
---|
125 | |
---|
126 | if (prod_id) |
---|
127 | return prod_id; |
---|
128 | |
---|
129 | /* Use OMAP32_ID_1 as fallback */ |
---|
130 | prod_id = ((jtag_id >> 12) & 0xffff); |
---|
131 | |
---|
132 | return prod_id; |
---|
133 | } |
---|
134 | |
---|
135 | // stl_raw is qemu related |
---|
136 | #define stl_raw(x,v) *(u32 *)(x) = v |
---|
137 | static void set_kernel_args(u32 ram_size, int initrd_size, const char *kernel_cmdline, u32 ram_start) |
---|
138 | { |
---|
139 | u32 *p; |
---|
140 | |
---|
141 | // Set pointer to kernel params |
---|
142 | p = (u32 *)(CFG_PARAMADDR); |
---|
143 | |
---|
144 | /* ATAG_CORE */ |
---|
145 | stl_raw(p++, 5); |
---|
146 | stl_raw(p++, 0x54410001); |
---|
147 | stl_raw(p++, 0); // 0 = ro ; 1= rw |
---|
148 | stl_raw(p++, 0x1000); // Page size |
---|
149 | stl_raw(p++, 0); // root device overrided by cmdline |
---|
150 | /* ATAG_MEM */ |
---|
151 | stl_raw(p++, 4); |
---|
152 | stl_raw(p++, 0x54410002); |
---|
153 | stl_raw(p++, ram_size); |
---|
154 | stl_raw(p++, ram_start); |
---|
155 | if (initrd_size) { |
---|
156 | /* ATAG_INITRD2 */ |
---|
157 | stl_raw(p++, 4); |
---|
158 | stl_raw(p++, 0x54420005); |
---|
159 | stl_raw(p++, ram_start + INITRD_LOAD_ADDR); |
---|
160 | stl_raw(p++, initrd_size); |
---|
161 | } |
---|
162 | if (kernel_cmdline && *kernel_cmdline) { |
---|
163 | /* ATAG_CMDLINE */ |
---|
164 | int cmdline_size; |
---|
165 | |
---|
166 | cmdline_size = strlen((char *)kernel_cmdline); |
---|
167 | memcpy ((char *)p + 2, (char *)kernel_cmdline, cmdline_size + 1); |
---|
168 | cmdline_size = (cmdline_size >> 2) + 1; |
---|
169 | stl_raw(p++, cmdline_size + 2); |
---|
170 | stl_raw(p++, 0x54410009); |
---|
171 | p += cmdline_size; |
---|
172 | } |
---|
173 | /* ATAG_END */ |
---|
174 | stl_raw(p++, 0); |
---|
175 | stl_raw(p++, 0); |
---|
176 | } |
---|
177 | |
---|
178 | #define C1_DC (1<<2) /* dcache off/on */ |
---|
179 | #define C1_IC (1<<12) /* icache off/on */ |
---|
180 | |
---|
181 | void cleanup_before_linux (void) |
---|
182 | { |
---|
183 | /* |
---|
184 | * this function is called just before we call linux |
---|
185 | * it prepares the processor for linux |
---|
186 | * |
---|
187 | * we turn off caches etc ... |
---|
188 | */ |
---|
189 | |
---|
190 | unsigned long i; |
---|
191 | |
---|
192 | /* turn off I/D-cache */ |
---|
193 | asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i)); |
---|
194 | i &= ~(C1_DC | C1_IC); |
---|
195 | asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i)); |
---|
196 | |
---|
197 | /* flush I/D-cache */ |
---|
198 | i = 0; |
---|
199 | asm ("mcr p15, 0, %0, c7, c7, 0": :"r" (i)); |
---|
200 | } |
---|
201 | |
---|
202 | void usb_crc32_pos( u32 pos, u32 total, u32 crc ) |
---|
203 | { |
---|
204 | u32 len = strcpy(usb_outbuffer,usb_hdr); |
---|
205 | usb_outbuffer[len-1] = 'x' ; |
---|
206 | (*(u32 *)(usb_outbuffer+len)) = pos ; |
---|
207 | len += sizeof(u32); |
---|
208 | (*(u32 *)(usb_outbuffer+len)) = total ; |
---|
209 | len += sizeof(u32); |
---|
210 | (*(u32 *)(usb_outbuffer+len)) = crc ; |
---|
211 | len += sizeof(u32); |
---|
212 | usb_send ( usb_outbuffer, len ); |
---|
213 | while (!usb_sent ()); |
---|
214 | } |
---|
215 | |
---|
216 | u32 _main (void) |
---|
217 | { |
---|
218 | u32 params = CFG_PARAMADDR ; |
---|
219 | u32 address = CFG_LOADADDR ; |
---|
220 | u32 crc = 0 ; |
---|
221 | usb_msg ('m', "In omap plateform"); |
---|
222 | |
---|
223 | u16 prod_id = omap_get_jtag_id(); |
---|
224 | switch (prod_id) |
---|
225 | { |
---|
226 | case 0xb55f: |
---|
227 | usb_msg ('m', "Found supported omap730"); |
---|
228 | break; |
---|
229 | default: |
---|
230 | usb_msg ('m', "Unsupported plateform found"); |
---|
231 | usb_code ('v', (u32) prod_id); |
---|
232 | break; |
---|
233 | } |
---|
234 | |
---|
235 | // Ready to manage requests |
---|
236 | usb_msg ('r', "Waiting first request"); |
---|
237 | |
---|
238 | for (;;) |
---|
239 | { |
---|
240 | u32 total, cmd, index = 0 ; |
---|
241 | |
---|
242 | u8 usb_inbuffer[CHUNK_SIZE]; |
---|
243 | usb_recv (usb_inbuffer, sizeof (usb_inbuffer)); |
---|
244 | while (!usb_rcvd ()); |
---|
245 | |
---|
246 | // Check we are knowing the provided header |
---|
247 | while ( usb_inbuffer[index] == (u8) usb_hdr[index] && usb_hdr[index] != '\0' ) |
---|
248 | index++ ; |
---|
249 | |
---|
250 | if ( usb_hdr[index] != '\0' ) |
---|
251 | continue ; |
---|
252 | |
---|
253 | cmd = usb_inbuffer[index++] ; |
---|
254 | |
---|
255 | if ((char)cmd == 's') // Ask size to upload |
---|
256 | { |
---|
257 | total= *(u32 *) &usb_inbuffer[index]; |
---|
258 | u32 size = 0; |
---|
259 | |
---|
260 | usb_code ('n', total); |
---|
261 | |
---|
262 | while (size < total) |
---|
263 | { |
---|
264 | u32 r; |
---|
265 | |
---|
266 | usb_recv ((u8 *)address, sizeof (usb_inbuffer)); |
---|
267 | while ((r= usb_rcvd ()) == 0); |
---|
268 | |
---|
269 | address += r ; |
---|
270 | size += r ; |
---|
271 | } |
---|
272 | |
---|
273 | usb_code ('o', size); |
---|
274 | |
---|
275 | } else |
---|
276 | if ((char)cmd == 'C') // set crc32 value for next download |
---|
277 | { |
---|
278 | crc = *(u32 *) &usb_inbuffer[index]; |
---|
279 | usb_code ('i', crc); |
---|
280 | |
---|
281 | } else |
---|
282 | if ((char)cmd == 'm') // Download size from memory |
---|
283 | { |
---|
284 | total= *(u32 *) &usb_inbuffer[index]; |
---|
285 | u32 size = 0; |
---|
286 | |
---|
287 | usb_code ('I', total); |
---|
288 | |
---|
289 | // Check crc32 if it has been set |
---|
290 | if (total && crc) |
---|
291 | { |
---|
292 | crc_cb_fnc_t* cb = NULL ; |
---|
293 | if (total>1024*1024) |
---|
294 | cb = &usb_crc32_pos ; |
---|
295 | u32 foundcrc = crc32(0, (char *)address,total,cb) ; |
---|
296 | usb_code ('C', foundcrc); |
---|
297 | // Skip download on crc match |
---|
298 | if (foundcrc == crc) |
---|
299 | total = 0 ; |
---|
300 | } |
---|
301 | |
---|
302 | if (total == 0) |
---|
303 | usb_blk ('D', (char *)address, 0); |
---|
304 | |
---|
305 | while (size < total) |
---|
306 | { |
---|
307 | u32 bs = MEM_READ_SIZE ; |
---|
308 | if ( total - size < MEM_READ_SIZE) |
---|
309 | bs = total - size ; |
---|
310 | usb_blk ('D', (char *)address, bs); |
---|
311 | address += bs ; |
---|
312 | size += bs ; |
---|
313 | } |
---|
314 | |
---|
315 | // reset crc for next download anyway |
---|
316 | crc = 0 ; |
---|
317 | |
---|
318 | } else |
---|
319 | if ((char)cmd == 'e') // End, just loop |
---|
320 | { |
---|
321 | usb_reset(); |
---|
322 | |
---|
323 | } else |
---|
324 | if ((char)cmd == 'f') // load file ACK |
---|
325 | { |
---|
326 | usb_msg ('f', (const char *)&usb_inbuffer[index]); |
---|
327 | |
---|
328 | } else |
---|
329 | if ((char)cmd == 'M') // dump memory command |
---|
330 | { |
---|
331 | usb_blk ('M', (char *)address, MEM_READ_SIZE); |
---|
332 | address += MEM_READ_SIZE ; |
---|
333 | |
---|
334 | } else |
---|
335 | if ((char)cmd == 'a') // set address command |
---|
336 | { |
---|
337 | address = *(u32 *) &usb_inbuffer[index]; |
---|
338 | usb_code ('i', address); |
---|
339 | |
---|
340 | } else |
---|
341 | if ((char)cmd == 'p') // do a poke on address |
---|
342 | { |
---|
343 | u32 size = memcpy( (char *) address, (char *) &usb_inbuffer[index], 4); |
---|
344 | address += size ; |
---|
345 | usb_code ('i', address ); |
---|
346 | |
---|
347 | } else |
---|
348 | if ((char)cmd == 'P') // do a peek on address |
---|
349 | { |
---|
350 | usb_code ('i', *(u32 *) address ); |
---|
351 | address += 4 ; |
---|
352 | |
---|
353 | } else |
---|
354 | if ((char)cmd == 'c') // call command |
---|
355 | { |
---|
356 | __asm__ __volatile__ ( |
---|
357 | "mov r0, #0 ;" |
---|
358 | "mov r1, #0 ;" |
---|
359 | "mov r2, #0 ;" |
---|
360 | "ldr r3, %1 ;" |
---|
361 | "blx r3 ;" |
---|
362 | "mov %0, r0 ;" |
---|
363 | : "=r"(index) : "m"(address) : "r0", "r1", "r2", "r3" |
---|
364 | ); |
---|
365 | usb_code ('i', index); |
---|
366 | |
---|
367 | } else |
---|
368 | if ((char)cmd == 'b') // boot command |
---|
369 | { |
---|
370 | usb_code ('b', address ); |
---|
371 | usb_reset(); |
---|
372 | __asm__ __volatile__ ( |
---|
373 | "ldr r0, %0 ;" |
---|
374 | "mov pc, r0 ;" |
---|
375 | : : "m"(address) |
---|
376 | ); |
---|
377 | |
---|
378 | } else |
---|
379 | if ((char)cmd == 'k') // boot kernel command |
---|
380 | { |
---|
381 | int machid = * (int *) &usb_inbuffer[index]; |
---|
382 | usb_code ('I', machid); |
---|
383 | set_kernel_args( RAM_SIZE, INITRD_SIZE, KERNEL_CMDLINE, CFG_BASESDRAM ); |
---|
384 | usb_code ('b', address ); |
---|
385 | usb_reset(); |
---|
386 | cleanup_before_linux(); |
---|
387 | __asm__ __volatile__ ( |
---|
388 | "mov r0, #0 ;" |
---|
389 | "ldr r1, %0 ;" |
---|
390 | "ldr r2, %1 ;" |
---|
391 | "mov r3, #0 ;" |
---|
392 | "mov r4, #0 ;" |
---|
393 | "ldr r5, %2 ;" |
---|
394 | "mov pc, r5 ;" |
---|
395 | : : "m" (machid), "m"(params), "m"(address) |
---|
396 | ); |
---|
397 | } else |
---|
398 | if ((char)cmd == 'S') // Stack command |
---|
399 | { |
---|
400 | __asm__ __volatile__ ( |
---|
401 | "ldr fp, %0 ;" |
---|
402 | "sub fp, #4 ;" |
---|
403 | "mov sp, fp ;" |
---|
404 | "sub sp, #256 ;" |
---|
405 | : : "m"(address) |
---|
406 | ); |
---|
407 | usb_code ('i', address); |
---|
408 | |
---|
409 | } |
---|
410 | } |
---|
411 | } |
---|