Fast boot from flash
[mw/milkymist.git] / software / bios / boot.c
1 /*
2  * Milkymist VJ SoC (Software)
3  * Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3 of the License.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <stdio.h>
19 #include <console.h>
20 #include <uart.h>
21 #include <system.h>
22 #include <board.h>
23 #include <crc.h>
24 #include <sfl.h>
25 #include <blockdev.h>
26 #include <fatfs.h>
27
28 #include <net/microudp.h>
29 #include <net/tftp.h>
30
31 #include <hw/hpdmc.h>
32
33 #include "boot.h"
34
35 extern const struct board_desc *brd_desc;
36
37 /*
38  * HACK: by defining this function as not inlinable, GCC will automatically
39  * put the values we want into the good registers because it has to respect
40  * the LM32 calling conventions.
41  */
42 static void __attribute__((noinline)) __attribute__((noreturn)) boot(unsigned int r1, unsigned int r2, unsigned int r3, unsigned int addr)
43 {
44         asm volatile( /* Invalidate instruction cache */
45                 "wcsr ICC, r0\n"
46                 "nop\n"
47                 "nop\n"
48                 "nop\n"
49                 "nop\n"
50                 "call r4\n"
51         );
52 }
53
54 /* Note that we do not use the hw timer so that this function works
55  * even if the system controller does not.
56  */
57 static int check_ack()
58 {
59         int timeout;
60         int recognized;
61         static const char str[SFL_MAGIC_LEN] = SFL_MAGIC_ACK;
62         
63         timeout = 2000000;
64         recognized = 0;
65         while(timeout > 0) {
66                 if(readchar_nonblock()) {
67                         char c;
68                         c = readchar();
69                         if(c == str[recognized]) {
70                                 recognized++;
71                                 if(recognized == SFL_MAGIC_LEN)
72                                         return 1;
73                         } else {
74                                 if(c == str[0])
75                                         recognized = 1;
76                                 else
77                                         recognized = 0;
78                         }
79                 }
80                 timeout--;
81         }
82         return 0;
83 }
84
85 #define MAX_FAILED 5
86
87 void serialboot()
88 {
89         struct sfl_frame frame;
90         int failed;
91         unsigned int cmdline_adr, initrdstart_adr, initrdend_adr;
92         
93         printf("I: Attempting serial firmware loading\n");
94         putsnonl(SFL_MAGIC_REQ);
95         if(!check_ack()) {
96                 printf("E: Timeout\n");
97                 return;
98         }
99         
100         failed = 0;
101         cmdline_adr = initrdstart_adr = initrdend_adr = 0;
102         while(1) {
103                 int i;
104                 int actualcrc;
105                 int goodcrc;
106                 
107                 /* Grab one frame */
108                 frame.length = readchar();
109                 frame.crc[0] = readchar();
110                 frame.crc[1] = readchar();
111                 frame.cmd = readchar();
112                 for(i=0;i<frame.length;i++)
113                         frame.payload[i] = readchar();
114                 
115                 /* Check CRC */
116                 actualcrc = ((int)frame.crc[0] << 8)|(int)frame.crc[1];
117                 goodcrc = crc16(&frame.cmd, frame.length+1);
118                 if(actualcrc != goodcrc) {
119                         failed++;
120                         if(failed == MAX_FAILED) {
121                                 printf("E: Too many consecutive errors, aborting");
122                                 return;
123                         }
124                         writechar(SFL_ACK_CRCERROR);
125                         continue;
126                 }
127                 
128                 /* CRC OK */
129                 switch(frame.cmd) {
130                         case SFL_CMD_ABORT:
131                                 failed = 0;
132                                 writechar(SFL_ACK_SUCCESS);
133                                 return;
134                         case SFL_CMD_LOAD: {
135                                 char *writepointer;
136                                 
137                                 failed = 0;
138                                 writepointer = (char *)(
139                                          ((unsigned int)frame.payload[0] << 24)
140                                         |((unsigned int)frame.payload[1] << 16)
141                                         |((unsigned int)frame.payload[2] << 8)
142                                         |((unsigned int)frame.payload[3] << 0));
143                                 for(i=4;i<frame.length;i++)
144                                         *(writepointer++) = frame.payload[i];
145                                 writechar(SFL_ACK_SUCCESS);
146                                 break;
147                         }
148                         case SFL_CMD_JUMP: {
149                                 unsigned int addr;
150                                 
151                                 failed = 0;
152                                 addr =  ((unsigned int)frame.payload[0] << 24)
153                                         |((unsigned int)frame.payload[1] << 16)
154                                         |((unsigned int)frame.payload[2] << 8)
155                                         |((unsigned int)frame.payload[3] << 0);
156                                 writechar(SFL_ACK_SUCCESS);
157                                 boot(cmdline_adr, initrdstart_adr, initrdend_adr, addr);
158                                 break;
159                         }
160                         case SFL_CMD_CMDLINE:
161                                 failed = 0;
162                                 cmdline_adr =  ((unsigned int)frame.payload[0] << 24)
163                                               |((unsigned int)frame.payload[1] << 16)
164                                               |((unsigned int)frame.payload[2] << 8)
165                                               |((unsigned int)frame.payload[3] << 0);
166                                 writechar(SFL_ACK_SUCCESS);
167                                 break;
168                         case SFL_CMD_INITRDSTART:
169                                 failed = 0;
170                                 initrdstart_adr =  ((unsigned int)frame.payload[0] << 24)
171                                                   |((unsigned int)frame.payload[1] << 16)
172                                                   |((unsigned int)frame.payload[2] << 8)
173                                                   |((unsigned int)frame.payload[3] << 0);
174                                 writechar(SFL_ACK_SUCCESS);
175                                 break;
176                         case SFL_CMD_INITRDEND:
177                                 failed = 0;
178                                 initrdend_adr =  ((unsigned int)frame.payload[0] << 24)
179                                                 |((unsigned int)frame.payload[1] << 16)
180                                                 |((unsigned int)frame.payload[2] << 8)
181                                                 |((unsigned int)frame.payload[3] << 0);
182                                 writechar(SFL_ACK_SUCCESS);
183                                 break;
184                         default:
185                                 failed++;
186                                 if(failed == MAX_FAILED) {
187                                         printf("E: Too many consecutive errors, aborting");
188                                         return;
189                                 }
190                                 writechar(SFL_ACK_UNKNOWN);
191                                 break;
192                 }
193         }
194 }
195
196 static unsigned char macadr[] = {0xf8, 0x71, 0xfe, 0x01, 0x02, 0x03};
197
198 #define LOCALIP1 192
199 #define LOCALIP2 168
200 #define LOCALIP3 0
201 #define LOCALIP4 42
202 #define REMOTEIP1 192
203 #define REMOTEIP2 168
204 #define REMOTEIP3 0
205 #define REMOTEIP4 14
206
207 static int tftp_get_v(unsigned int ip, const char *filename, char *buffer)
208 {
209         int r;
210
211         r = tftp_get(ip, filename, buffer);
212         if(r > 0)
213                 printf("I: Successfully downloaded %d bytes from %s over TFTP\n", r, filename);
214         else
215                 printf("I: Unable to download %s over TFTP\n", filename);
216         return r;
217 }
218
219 static char microudp_buf[MICROUDP_BUFSIZE];
220
221 void netboot()
222 {
223         int size;
224         unsigned int cmdline_adr, initrdstart_adr, initrdend_adr;
225         unsigned int ip;
226
227         printf("I: Booting from network...\n");
228         printf("I: MAC      : %02x:%02x:%02x:%02x:%02x:%02x\n", macadr[0], macadr[1], macadr[2], macadr[3], macadr[4], macadr[5]);
229         printf("I: Local IP : %d.%d.%d.%d\n", LOCALIP1, LOCALIP2, LOCALIP3, LOCALIP4);
230         printf("I: Remote IP: %d.%d.%d.%d\n", REMOTEIP1, REMOTEIP2, REMOTEIP3, REMOTEIP4);
231
232         ip = IPTOINT(REMOTEIP1, REMOTEIP2, REMOTEIP3, REMOTEIP4);
233         
234         microudp_start(macadr, IPTOINT(LOCALIP1, LOCALIP2, LOCALIP3, LOCALIP4), microudp_buf);
235         
236         if(tftp_get_v(ip, "boot.bin", (void *)SDRAM_BASE) <= 0) {
237                 printf("E: Network boot failed\n");
238                 return;
239         }
240         
241         cmdline_adr = SDRAM_BASE+0x1000000;
242         size = tftp_get_v(ip, "cmdline.txt", (void *)cmdline_adr);
243         if(size <= 0) {
244                 printf("I: No command line parameters found\n");
245                 cmdline_adr = 0;
246         } else
247                 *((char *)(cmdline_adr+size)) = 0x00;
248
249         initrdstart_adr = SDRAM_BASE+0x1002000;
250         size = tftp_get_v(ip, "initrd.bin", (void *)initrdstart_adr);
251         if(size <= 0) {
252                 printf("I: No initial ramdisk found\n");
253                 initrdstart_adr = 0;
254                 initrdend_adr = 0;
255         } else
256                 initrdend_adr = initrdstart_adr + size - 1;
257
258         microudp_shutdown();
259
260         printf("I: Booting...\n");
261         boot(cmdline_adr, initrdstart_adr, initrdend_adr, SDRAM_BASE);
262 }
263
264 static int tryload(char *filename, unsigned int address)
265 {
266         int devsize, realsize;
267
268         devsize = fatfs_load(filename, (char *)address, 16*1024*1024, &realsize);
269         if(devsize <= 0)
270                 return -1;
271         if(realsize > devsize) {
272                 printf("E: File size larger than the blocks read (corrupted FS or IO error ?)\n");
273                 return -1;
274         }
275         printf("I: Read a %d byte image from %s\n", realsize, filename);
276
277         return realsize;
278 }
279
280 void fsboot()
281 {
282         int size;
283         unsigned int cmdline_adr, initrdstart_adr, initrdend_adr;
284
285         printf("I: Booting from filesystem...\n");
286         if(!fatfs_init(BLOCKDEV_FLASH, 0)) {
287                 printf("E: Unable to initialize filesystem\n");
288                 return;
289         }
290
291         if(tryload("BOOT.BIN", SDRAM_BASE) <= 0) {
292                 printf("E: Firmware image not found\n");
293                 fatfs_done();
294                 return;
295         }
296
297         cmdline_adr = SDRAM_BASE+0x1000000;
298         size = tryload("CMDLINE.TXT", cmdline_adr);
299         if(size <= 0) {
300                 printf("I: No command line parameters found (CMDLINE.TXT)\n");
301                 cmdline_adr = 0;
302         } else
303                 *((char *)(cmdline_adr+size)) = 0x00;
304
305         initrdstart_adr = SDRAM_BASE+0x1002000;
306         size = tryload("INITRD.BIN", initrdstart_adr);
307         if(size <= 0) {
308                 printf("I: No initial ramdisk found (INITRD.BIN)\n");
309                 initrdstart_adr = 0;
310                 initrdend_adr = 0;
311         } else
312                 initrdend_adr = initrdstart_adr + size - 1;
313
314         fatfs_done();
315         printf("I: Booting...\n");
316         boot(cmdline_adr, initrdstart_adr, initrdend_adr, SDRAM_BASE);
317 }