TFTP boot working
[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 <cffat.h>
24 #include <crc.h>
25 #include <sfl.h>
26
27 #include <net/microudp.h>
28 #include <net/tftp.h>
29
30 #include <hw/hpdmc.h>
31
32 #include "boot.h"
33
34 extern const struct board_desc *brd_desc;
35
36 /*
37  * HACK: by defining this function as not inlinable, GCC will automatically
38  * put the values we want into the good registers because it has to respect
39  * the LM32 calling conventions.
40  */
41 static void __attribute__((noinline)) __attribute__((noreturn)) boot(unsigned int r1, unsigned int r2, unsigned int r3, unsigned int addr)
42 {
43         asm volatile( /* Invalidate instruction cache */
44                 "wcsr ICC, r0\n"
45                 "nop\n"
46                 "nop\n"
47                 "nop\n"
48                 "nop\n"
49                 "call r4\n"
50         );
51 }
52
53 /* Note that we do not use the hw timer so that this function works
54  * even if the system controller does not.
55  */
56 static int check_ack()
57 {
58         int timeout;
59         int recognized;
60         static const char str[SFL_MAGIC_LEN] = SFL_MAGIC_ACK;
61         
62         timeout = 4500000;
63         recognized = 0;
64         while(timeout > 0) {
65                 if(readchar_nonblock()) {
66                         char c;
67                         c = readchar();
68                         if(c == str[recognized]) {
69                                 recognized++;
70                                 if(recognized == SFL_MAGIC_LEN)
71                                         return 1;
72                         } else {
73                                 if(c == str[0])
74                                         recognized = 1;
75                                 else
76                                         recognized = 0;
77                         }
78                 }
79                 timeout--;
80         }
81         return 0;
82 }
83
84 #define MAX_FAILED 5
85
86 void serialboot()
87 {
88         struct sfl_frame frame;
89         int failed;
90         unsigned int cmdline_adr, initrdstart_adr, initrdend_adr;
91         
92         printf("I: Attempting serial firmware loading\n");
93         putsnonl(SFL_MAGIC_REQ);
94         if(!check_ack()) {
95                 printf("E: Timeout\n");
96                 return;
97         }
98         
99         failed = 0;
100         cmdline_adr = initrdstart_adr = initrdend_adr = 0;
101         while(1) {
102                 int i;
103                 int actualcrc;
104                 int goodcrc;
105                 
106                 /* Grab one frame */
107                 frame.length = readchar();
108                 frame.crc[0] = readchar();
109                 frame.crc[1] = readchar();
110                 frame.cmd = readchar();
111                 for(i=0;i<frame.length;i++)
112                         frame.payload[i] = readchar();
113                 
114                 /* Check CRC */
115                 actualcrc = ((int)frame.crc[0] << 8)|(int)frame.crc[1];
116                 goodcrc = crc16(&frame.cmd, frame.length+1);
117                 if(actualcrc != goodcrc) {
118                         failed++;
119                         if(failed == MAX_FAILED) {
120                                 printf("E: Too many consecutive errors, aborting");
121                                 return;
122                         }
123                         writechar(SFL_ACK_CRCERROR);
124                         continue;
125                 }
126                 
127                 /* CRC OK */
128                 switch(frame.cmd) {
129                         case SFL_CMD_ABORT:
130                                 failed = 0;
131                                 writechar(SFL_ACK_SUCCESS);
132                                 return;
133                         case SFL_CMD_LOAD: {
134                                 char *writepointer;
135                                 
136                                 failed = 0;
137                                 writepointer = (char *)(
138                                          ((unsigned int)frame.payload[0] << 24)
139                                         |((unsigned int)frame.payload[1] << 16)
140                                         |((unsigned int)frame.payload[2] << 8)
141                                         |((unsigned int)frame.payload[3] << 0));
142                                 for(i=4;i<frame.length;i++)
143                                         *(writepointer++) = frame.payload[i];
144                                 writechar(SFL_ACK_SUCCESS);
145                                 break;
146                         }
147                         case SFL_CMD_JUMP: {
148                                 unsigned int addr;
149                                 
150                                 failed = 0;
151                                 addr =  ((unsigned int)frame.payload[0] << 24)
152                                         |((unsigned int)frame.payload[1] << 16)
153                                         |((unsigned int)frame.payload[2] << 8)
154                                         |((unsigned int)frame.payload[3] << 0);
155                                 writechar(SFL_ACK_SUCCESS);
156                                 boot(cmdline_adr, initrdstart_adr, initrdend_adr, addr);
157                                 break;
158                         }
159                         case SFL_CMD_CMDLINE:
160                                 failed = 0;
161                                 cmdline_adr =  ((unsigned int)frame.payload[0] << 24)
162                                               |((unsigned int)frame.payload[1] << 16)
163                                               |((unsigned int)frame.payload[2] << 8)
164                                               |((unsigned int)frame.payload[3] << 0);
165                                 writechar(SFL_ACK_SUCCESS);
166                                 break;
167                         case SFL_CMD_INITRDSTART:
168                                 failed = 0;
169                                 initrdstart_adr =  ((unsigned int)frame.payload[0] << 24)
170                                                   |((unsigned int)frame.payload[1] << 16)
171                                                   |((unsigned int)frame.payload[2] << 8)
172                                                   |((unsigned int)frame.payload[3] << 0);
173                                 writechar(SFL_ACK_SUCCESS);
174                                 break;
175                         case SFL_CMD_INITRDEND:
176                                 failed = 0;
177                                 initrdend_adr =  ((unsigned int)frame.payload[0] << 24)
178                                                 |((unsigned int)frame.payload[1] << 16)
179                                                 |((unsigned int)frame.payload[2] << 8)
180                                                 |((unsigned int)frame.payload[3] << 0);
181                                 writechar(SFL_ACK_SUCCESS);
182                                 break;
183                         default:
184                                 failed++;
185                                 if(failed == MAX_FAILED) {
186                                         printf("E: Too many consecutive errors, aborting");
187                                         return;
188                                 }
189                                 writechar(SFL_ACK_UNKNOWN);
190                                 break;
191                 }
192         }
193 }
194
195 static unsigned char macadr[] = {0xf8, 0x71, 0xfe, 0x01, 0x02, 0x03};
196
197 void netboot()
198 {
199         int size;
200         unsigned int cmdline_adr, initrdstart_adr, initrdend_adr;
201         unsigned int ip;
202
203         printf("I: Booting from network...\n");
204         printf("I: MAC      : %02x:%02x:%02x:%02x:%02x:%02x\n", macadr[0], macadr[1], macadr[2], macadr[3], macadr[4], macadr[5]);
205         printf("I: Local IP : 192.168.0.42\n");
206         printf("I: Remote IP: 192.168.0.14\n");
207
208         ip = IPTOINT(192,168,0,14);
209         
210         microudp_start(macadr, IPTOINT(192,168,0,42), (void *)(SDRAM_BASE+1024*1024*(brd_desc->sdram_size-2)));
211         
212         if(tftp_get(ip, "boot.bin", (void *)SDRAM_BASE) <= 0) {
213                 printf("E: Unable to download boot.bin\n");
214                 return;
215         }
216         
217         cmdline_adr = SDRAM_BASE+0x1000000;
218         if(tftp_get(ip, "cmdline.txt", (void *)cmdline_adr) <= 0) {
219                 printf("I: No command line parameters found\n");
220                 cmdline_adr = 0;
221         }
222
223         initrdstart_adr = SDRAM_BASE+0x1002000;
224         size = tftp_get(ip, "initrd.bin", (void *)initrdstart_adr);
225         if(size <= 0) {
226                 printf("I: No initial ramdisk found\n");
227                 initrdstart_adr = 0;
228                 initrdend_adr = 0;
229         } else
230                 initrdend_adr = initrdstart_adr + size - 1;
231
232         microudp_shutdown();
233
234         printf("I: Booting...\n");
235         boot(cmdline_adr, initrdstart_adr, initrdend_adr, SDRAM_BASE);
236 }
237
238 static int tryload(char *filename, unsigned int address)
239 {
240         int devsize, realsize;
241         
242         devsize = cffat_load(filename, (char *)address, 16*1024*1024, &realsize);
243         if(devsize <= 0)
244                 return -1;
245         if(realsize > devsize) {
246                 printf("E: File size larger than the blocks read (corrupted FS or IO error ?)\n");
247                 cffat_done();
248                 return -1;
249         }
250         printf("I: Read a %d byte image from %s\n", realsize, filename);
251         
252         return realsize;
253 }
254
255
256 void cardboot(int alt)
257 {
258         int size;
259         unsigned int cmdline_adr, initrdstart_adr, initrdend_adr;
260
261         if(brd_desc->memory_card == MEMCARD_NONE) {
262                 printf("E: No memory card on this board\n");
263                 return;
264         }
265         
266         printf("I: Booting from CF card...\n");
267         if(!cffat_init()) {
268                 printf("E: Unable to initialize filesystem\n");
269                 return;
270         }
271
272         if(tryload(alt ? "ALTBOOT.BIN" : "BOOT.BIN", SDRAM_BASE) <= 0) {
273                 printf("E: Firmware image not found\n");
274                 return;
275         }
276
277         cmdline_adr = SDRAM_BASE+0x1000000;
278         if(tryload("CMDLINE.TXT", cmdline_adr) <= 0) {
279                 printf("I: No command line parameters found (CMDLINE.TXT)\n");
280                 cmdline_adr = 0;
281         }
282
283         initrdstart_adr = SDRAM_BASE+0x1002000;
284         size = tryload("INITRD.BIN", initrdstart_adr);
285         if(size <= 0) {
286                 printf("I: No initial ramdisk found (INITRD.BIN)\n");
287                 initrdstart_adr = 0;
288                 initrdend_adr = 0;
289         } else
290                 initrdend_adr = initrdstart_adr + size - 1;
291
292         cffat_done();
293         printf("I: Booting...\n");
294         boot(cmdline_adr, initrdstart_adr, initrdend_adr, SDRAM_BASE);
295 }