]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - common/cmd_bootce.c
48617dd76d6852ec5ba5b96c5fd4401e9269e568
[karo-tx-uboot.git] / common / cmd_bootce.c
1 /*
2  * Copyright (C) 2012 Lothar Waßmann <LW@KARO-electronics.de>
3  * based on: code from RedBoot (C) Uwe Steinkohl <US@KARO-electronics.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
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (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,
21  * MA 02111-1307 USA
22  */
23
24 #include <common.h>
25 #include <command.h>
26 #include <net.h>
27 #include <wince.h>
28 #include <asm/errno.h>
29
30 DECLARE_GLOBAL_DATA_PTR;
31
32 #define WINCE_VRAM_BASE         0x80000000
33 #define CE_FIX_ADDRESS(a)       ((void *)((a) - WINCE_VRAM_BASE + CONFIG_SYS_SDRAM_BASE))
34
35 #ifndef INT_MAX
36 #define INT_MAX                 ((1U << (sizeof(int) * 8 - 1)) - 1)
37 #endif
38
39 /* Bin image parse states */
40 #define CE_PS_RTI_ADDR          0
41 #define CE_PS_RTI_LEN           1
42 #define CE_PS_E_ADDR            2
43 #define CE_PS_E_LEN             3
44 #define CE_PS_E_CHKSUM          4
45 #define CE_PS_E_DATA            5
46
47 #define CE_MIN(a, b)            (((a) < (b)) ? (a) : (b))
48 #define CE_MAX(a, b)            (((a) > (b)) ? (a) : (b))
49
50 #define _STRMAC(s)              #s
51 #define STRMAC(s)               _STRMAC(s)
52
53 static ce_bin __attribute__ ((aligned (32))) g_bin;
54 static ce_net __attribute__ ((aligned (32))) g_net;
55
56 static inline void print_IPaddr(IPaddr_t ip)
57 {
58         printf("%d.%d.%d.%d",
59                 ip & 0xff,
60                 (ip >> 8) & 0xff,
61                 (ip >> 16) & 0xff,
62                 (ip >> 24) & 0xff);
63 }
64
65 static void ce_init_bin(ce_bin *bin, unsigned char *dataBuffer)
66 {
67         memset(bin, 0, sizeof(*bin));
68
69         bin->data = dataBuffer;
70         bin->parseState = CE_PS_RTI_ADDR;
71         bin->parsePtr = (unsigned char *)bin;
72 }
73
74 static int ce_is_bin_image(void *image, int imglen)
75 {
76         if (imglen < CE_BIN_SIGN_LEN) {
77                 return 0;
78         }
79
80         return memcmp(image, CE_BIN_SIGN, CE_BIN_SIGN_LEN) == 0;
81 }
82
83 static const struct ce_magic {
84         char magic[8];
85         size_t size;
86         ce_std_driver_globals drv_glb;
87 } ce_magic_template = {
88         .magic = "KARO_CE6",
89         .size = sizeof(ce_std_driver_globals),
90         .drv_glb = {
91                 .header = {
92                         .signature = STD_DRV_GLB_SIGNATURE,
93                         .oalVersion = 1,
94                         .bspVersion = 1,
95                 },
96         },
97 };
98
99 #ifdef DEBUG
100 static void __attribute__((unused)) ce_dump_block(void *ptr, int length)
101 {
102         char *p = ptr;
103         int i;
104         int j;
105
106         for (i = 0; i < length; i++) {
107                 if (!(i % 16)) {
108                         printf("\n%p: ", ptr + i);
109                 }
110
111                 printf("%02x ", p[i]);
112                 if (!((i + 1) % 16)){
113                         printf("      ");
114                         for (j = i - 15; j <= i; j++){
115                                 if((p[j] > 0x1f) && (p[j] < 0x7f)) {
116                                         printf("%c", p[j]);
117                                 } else {
118                                         printf(".");
119                                 }
120                         }
121                 }
122         }
123         printf("\n");
124 }
125 #else
126 static inline void ce_dump_block(void *ptr, int length)
127 {
128 }
129 #endif
130
131 static void ce_setup_std_drv_globals(ce_std_driver_globals *std_drv_glb,
132                                 ce_bin *bin)
133 {
134         if (eth_get_dev()) {
135                 memcpy(&std_drv_glb->kitl.mac, eth_get_dev()->enetaddr,
136                         sizeof(std_drv_glb->kitl.mac));
137         }
138         snprintf(std_drv_glb->deviceId, sizeof(std_drv_glb->deviceId),
139                 "Triton%02X", std_drv_glb->kitl.mac[2] & 0xff);
140
141         std_drv_glb->kitl.ipAddress = gd->bd->bi_ip_addr;
142         std_drv_glb->kitl.ipMask = getenv_IPaddr("netmask");
143         std_drv_glb->kitl.ipRoute = getenv_IPaddr("gatewayip");
144         std_drv_glb->mtdparts = getenv("mtdparts");
145 }
146
147 static void ce_prepare_run_bin(ce_bin *bin)
148 {
149         ce_driver_globals *drv_glb;
150         struct ce_magic *ce_magic = (void *)CONFIG_SYS_SDRAM_BASE + 0x160;
151         ce_std_driver_globals *std_drv_glb = &ce_magic->drv_glb;
152
153         /* Clear os RAM area (if needed) */
154         if (bin->edbgConfig.flags & EDBG_FL_CLEANBOOT) {
155                 debug("cleaning memory from %p to %p\n",
156                         bin->eRamStart, bin->eRamStart + bin->eRamLen);
157
158                 printf("Preparing clean boot ... ");
159                 memset(bin->eRamStart, 0, bin->eRamLen);
160                 printf("ok\n");
161         }
162
163         /* Prepare driver globals (if needed) */
164         if (bin->eDrvGlb) {
165                 debug("Copying CE MAGIC from %p to %p..%p\n",
166                         &ce_magic_template, ce_magic, (void *)ce_magic + sizeof(*ce_magic));
167                 memcpy(ce_magic, &ce_magic_template, sizeof(*ce_magic));
168
169                 ce_setup_std_drv_globals(std_drv_glb, bin);
170                 ce_dump_block(ce_magic, sizeof(*ce_magic));
171
172                 drv_glb = bin->eDrvGlb;
173                 memset(drv_glb, 0, sizeof(*drv_glb));
174
175                 drv_glb->signature = DRV_GLB_SIGNATURE;
176
177                 /* Local ethernet MAC address */
178                 memcpy(drv_glb->macAddr, std_drv_glb->kitl.mac,
179                         sizeof(drv_glb->macAddr));
180                 debug("got MAC address %02x:%02x:%02x:%02x:%02x:%02x from environment\n",
181                         drv_glb->macAddr[0], drv_glb->macAddr[1],
182                         drv_glb->macAddr[2], drv_glb->macAddr[3],
183                         drv_glb->macAddr[4], drv_glb->macAddr[5]);
184
185                 /* Local IP address */
186                 drv_glb->ipAddr = getenv_IPaddr("ipaddr");
187
188                 /* Subnet mask */
189                 drv_glb->ipMask = getenv_IPaddr("netmask");
190
191                 /* Gateway config */
192                 drv_glb->ipGate = getenv_IPaddr("gatewayip");
193 #ifdef DEBUG
194                 debug("got IP address ");
195                 print_IPaddr(drv_glb->ipAddr);
196                 debug(" from environment\n");
197                 debug("got IP mask ");
198                 print_IPaddr(drv_glb->ipMask);
199                 debug(" from environment\n");
200                 debug("got gateway address ");
201                 print_IPaddr(drv_glb->ipGate);
202                 debug(" from environment\n");
203 #endif
204                 /* EDBG services config */
205                 memcpy(&drv_glb->edbgConfig, &bin->edbgConfig, sizeof(bin->edbgConfig));
206         }
207
208         /*
209          * Make sure, all the above makes it into SDRAM because
210          * WinCE switches the cache & MMU off, obviously without
211          * flushing it first!
212          */
213         flush_dcache_all();
214 }
215
216 static int ce_lookup_ep_bin(ce_bin *bin)
217 {
218         ce_rom_hdr *header;
219         ce_toc_entry *tentry;
220         e32_rom *e32;
221         unsigned int i;
222         uint32_t *sig = (uint32_t *)(bin->rtiPhysAddr + ROM_SIGNATURE_OFFSET);
223
224         debug("Looking for TOC signature at %p\n", sig);
225
226         /* Check image Table Of Contents (TOC) signature */
227         if (*sig != ROM_SIGNATURE) {
228                 printf("Error: Did not find image TOC signature!\n");
229                 printf("Expected %08x at address %p; found %08x instead\n",
230                         ROM_SIGNATURE, sig, *sig);
231                 return 0;
232         }
233
234         /* Lookup entry point */
235         header = CE_FIX_ADDRESS(*(unsigned int *)(bin->rtiPhysAddr +
236                                                 ROM_SIGNATURE_OFFSET +
237                                                 sizeof(unsigned int)));
238         tentry = (ce_toc_entry *)(header + 1);
239
240         for (i = 0; i < header->nummods; i++) {
241                 // Look for 'nk.exe' module
242                 if (strcmp(CE_FIX_ADDRESS(tentry[i].fileName), "nk.exe") == 0) {
243                         // Save entry point and RAM addresses
244
245                         e32 = CE_FIX_ADDRESS(tentry[i].e32Offset);
246
247                         bin->eEntryPoint = CE_FIX_ADDRESS(tentry[i].loadOffset) +
248                                 e32->e32_entryrva;
249                         bin->eRamStart = CE_FIX_ADDRESS(header->ramStart);
250                         bin->eRamLen = header->ramEnd - header->ramStart;
251                         // Save driver_globals address
252                         // Must follow RAM section in CE config.bib file
253                         //
254                         // eg.
255                         //
256                         // RAM          80900000        03200000        RAM
257                         // DRV_GLB      83B00000        00001000        RESERVED
258                         //
259                         bin->eDrvGlb = CE_FIX_ADDRESS(header->ramEnd);
260                         return 1;
261                 }
262         }
263
264         // Error: Did not find 'nk.exe' module
265         return 0;
266 }
267
268 static int ce_parse_bin(ce_bin *bin)
269 {
270         unsigned char *pbData = bin->data;
271         int len = bin->dataLen;
272         int copyLen;
273
274         debug("starting ce image parsing:\n\tbin->binLen: 0x%08X\n", bin->binLen);
275
276         if (len) {
277                 if (bin->binLen == 0) {
278                         // Check for the .BIN signature first
279                         if (!ce_is_bin_image(pbData, len)) {
280                                 printf("Error: Invalid or corrupted .BIN image!\n");
281                                 return CE_PR_ERROR;
282                         }
283
284                         printf("Loading Windows CE .BIN image ...\n");
285                         // Skip signature
286                         len -= CE_BIN_SIGN_LEN;
287                         pbData += CE_BIN_SIGN_LEN;
288                 }
289
290                 while (len) {
291                         switch (bin->parseState) {
292                         case CE_PS_RTI_ADDR:
293                         case CE_PS_RTI_LEN:
294                         case CE_PS_E_ADDR:
295                         case CE_PS_E_LEN:
296                         case CE_PS_E_CHKSUM:
297                                 copyLen = CE_MIN(sizeof(unsigned int) - bin->parseLen, len);
298                                 memcpy(&bin->parsePtr[bin->parseLen], pbData, copyLen);
299
300                                 bin->parseLen += copyLen;
301                                 len -= copyLen;
302                                 pbData += copyLen;
303
304                                 if (bin->parseLen == sizeof(unsigned int)) {
305                                         if (bin->parseState == CE_PS_RTI_ADDR)
306                                                 bin->rtiPhysAddr = CE_FIX_ADDRESS(bin->rtiPhysAddr);
307                                         else if (bin->parseState == CE_PS_E_ADDR &&
308                                                 bin->ePhysAddr)
309                                                 bin->ePhysAddr = CE_FIX_ADDRESS(bin->ePhysAddr);
310
311                                         bin->parseState++;
312                                         bin->parseLen = 0;
313                                         bin->parsePtr += sizeof(unsigned int);
314
315                                         if (bin->parseState == CE_PS_E_DATA) {
316                                                 if (bin->ePhysAddr) {
317                                                         bin->parsePtr = bin->ePhysAddr;
318                                                         bin->parseChkSum = 0;
319                                                 } else {
320                                                         /* EOF */
321                                                         len = 0;
322                                                         bin->endOfBin = 1;
323                                                 }
324                                         }
325                                 }
326                                 break;
327
328                         case CE_PS_E_DATA:
329                                 if (bin->ePhysAddr) {
330                                         copyLen = CE_MIN(bin->ePhysLen - bin->parseLen, len);
331                                         bin->parseLen += copyLen;
332                                         len -= copyLen;
333
334                                         while (copyLen--) {
335                                                 bin->parseChkSum += *pbData;
336                                                 *bin->parsePtr++ = *pbData++;
337                                         }
338
339                                         if (bin->parseLen == bin->ePhysLen) {
340                                                 printf("Section [%02d]: address %p, size 0x%08X, checksum %s\n",
341                                                         bin->section,
342                                                         bin->ePhysAddr,
343                                                         bin->ePhysLen,
344                                                         (bin->eChkSum == bin->parseChkSum) ? "ok" : "fail");
345
346                                                 if (bin->eChkSum != bin->parseChkSum) {
347                                                         printf("Error: Checksum error, corrupted .BIN file!\n");
348                                                         printf("checksum calculated: 0x%08x from file: 0x%08x\n",
349                                                                 bin->parseChkSum, bin->eChkSum);
350                                                         bin->binLen = 0;
351                                                         return CE_PR_ERROR;
352                                                 }
353
354                                                 bin->section++;
355                                                 bin->parseState = CE_PS_E_ADDR;
356                                                 bin->parseLen = 0;
357                                                 bin->parsePtr = (unsigned char *)&bin->ePhysAddr;
358                                         }
359                                 } else {
360                                         bin->parseLen = 0;
361                                         bin->endOfBin = 1;
362                                         len = 0;
363                                 }
364                                 break;
365                         }
366                 }
367         }
368
369         if (bin->endOfBin) {
370                 if (!ce_lookup_ep_bin(bin)) {
371                         printf("Error: entry point not found!\n");
372                         bin->binLen = 0;
373                         return CE_PR_ERROR;
374                 }
375
376                 printf("Entry point: %p, address range: %p-%p\n",
377                         bin->eEntryPoint,
378                         bin->rtiPhysAddr,
379                         bin->rtiPhysAddr + bin->rtiPhysLen);
380
381                 return CE_PR_EOF;
382         }
383
384         /* Need more data */
385         bin->binLen += bin->dataLen;
386         return CE_PR_MORE;
387 }
388
389 static int ce_bin_load(void *image, int imglen)
390 {
391         ce_init_bin(&g_bin, image);
392         g_bin.dataLen = imglen;
393         if (ce_parse_bin(&g_bin) == CE_PR_EOF) {
394                 ce_prepare_run_bin(&g_bin);
395                 return 1;
396         }
397
398         return 0;
399 }
400
401 typedef void (*CeEntryPointPtr)(void);
402
403 static void ce_run_bin(ce_bin *bin)
404 {
405         CeEntryPointPtr EnrtryPoint;
406
407         printf("Launching Windows CE ...\n");
408
409         EnrtryPoint = bin->eEntryPoint;
410         EnrtryPoint();
411 }
412
413 static int do_bootce(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
414 {
415         void *addr;
416         size_t image_size;
417         char *s;
418
419         if (argc > 1) {
420                 addr = (void *)simple_strtoul(argv[1], NULL, 16);
421                 image_size = INT_MAX;           /* actually we do not know the image size */
422         } else if (getenv("fileaddr") != NULL) {
423                 addr = (void *)getenv_ulong("fileaddr", 16, 0);
424                 image_size = getenv_ulong("filesize", 16, INT_MAX);
425         } else {
426                 printf ("Usage:\n%s\n", cmdtp->usage);
427                 return 1;
428         }
429
430         printf ("## Booting Windows CE Image from address %p ...\n", addr);
431
432         /* check if there is a valid windows CE image */
433         if (ce_is_bin_image(addr, image_size)) {
434                 if (!ce_bin_load(addr, image_size)) {
435                         /* Ops! Corrupted .BIN image! */
436                         /* Handle error here ...      */
437                         printf("corrupted .BIN image !!!\n");
438                         return 1;
439                 }
440                 if ((s = getenv("autostart")) != NULL) {
441                         if (*s != 'y') {
442                                 /*
443                                  * just use bootce to load the image to SDRAM;
444                                  * Do not start it automatically.
445                                  */
446                                 setenv_addr("fileaddr",
447                                         g_bin.eEntryPoint);
448                                 return 0;
449                         }
450                 }
451                 ce_run_bin(&g_bin);             /* start the image */
452         } else {
453                 printf("Image does not seem to be a valid Windows CE image!\n");
454                 return 1;
455         }
456         return 1;       /* never reached - just to keep compiler happy */
457 }
458
459 U_BOOT_CMD(
460         bootce, 2,      0,      do_bootce,
461         "bootce\t- Boot a Windows CE image from memory \n",
462         "[args..]\n"
463         "\taddr\t\t-boot image from address addr\n"
464 );
465
466 static void wince_handler(uchar *pkt, unsigned dport, IPaddr_t sip,
467                         unsigned sport, unsigned len)
468 {
469         NetState = NETLOOP_SUCCESS;     /* got input - quit net loop */
470
471         if (memcmp(&g_net.data[g_net.align_offset],
472                         eth_get_dev()->enetaddr, ETH_ALEN) == 0) {
473                 g_net.got_packet_4me = 1;
474                 g_net.dataLen = len;
475         } else {
476                 g_net.got_packet_4me = 0;
477                 return;
478         }
479
480         g_net.srvAddrRecv.sin_port = *((unsigned short *)(&g_net.data[
481                         ETHER_HDR_SIZE + IP_HDR_SIZE_NO_UDP + g_net.align_offset]));
482         NetCopyIP(&g_net.srvAddrRecv.sin_addr, &g_net.data[ETHER_HDR_SIZE +
483                 g_net.align_offset + 12]);
484         memcpy(NetServerEther, &g_net.data[g_net.align_offset + 6], ETH_ALEN);
485 #if 0
486         printf("received packet:   buffer %p   Laenge %d \n", pkt, len);
487         printf("from ");
488         print_IPaddr(g_net.srvAddrRecv.sin_addr);
489         printf(", port: %d\n", ntohs(g_net.srvAddrRecv.sin_port));
490
491         ce_dump_block(pkt, len);
492
493         printf("Headers:\n");
494         ce_dump_block(pkt - ETHER_HDR_SIZE - IP_HDR_SIZE, ETHER_HDR_SIZE + IP_HDR_SIZE);
495         printf("my port should be: %d\n",
496                 ntohs(*((unsigned short *)(&g_net.data[ETHER_HDR_SIZE +
497                                                         IP_HDR_SIZE_NO_UDP +
498                                                         g_net.align_offset + 2]))));
499 #endif
500 }
501
502 /* returns packet length if successfull */
503 static int ce_recv_packet(uchar *buf, int len, struct sockaddr_in *from,
504                 struct sockaddr_in *local, struct timeval *timeout)
505 {
506         int rxlength;
507         ulong time_started;
508
509         g_net.got_packet_4me = 0;
510         time_started = get_timer(0);
511
512         NetRxPackets[0] = buf;
513         NetSetHandler(wince_handler);
514
515         while (1) {
516                 rxlength = eth_rx();
517                 if (g_net.got_packet_4me)
518                         return g_net.dataLen;
519                 /* check for timeout */
520                 if (get_timer(time_started) > timeout->tv_sec * CONFIG_SYS_HZ) {
521                         return -ETIMEDOUT;
522                 }
523         }
524 }
525
526 static int ce_recv_frame(ce_net *net, int timeout)
527 {
528         struct timeval timeo;
529
530         timeo.tv_sec = timeout;
531         timeo.tv_usec = 0;
532
533         net->dataLen = ce_recv_packet(&net->data[net->align_offset],
534                                 sizeof(net->data) - net->align_offset,
535                                 &net->srvAddrRecv, &net->locAddr, &timeo);
536
537         if (net->dataLen < 0 || net->dataLen > sizeof(net->data)) {
538                 /* Error! No data available */
539                 net->dataLen = 0;
540         }
541
542         return net->dataLen;
543 }
544
545 static int ce_send_frame(ce_net *net)
546 {
547         /* Send UDP packet */
548         NetTxPacket = &net->data[net->align_offset];
549         return NetSendUDPPacket(NetServerEther, net->srvAddrSend.sin_addr,
550                                 ntohs(net->srvAddrSend.sin_port),
551                                 ntohs(net->locAddr.sin_port), net->dataLen);
552 }
553
554 static int ce_send_write_ack(ce_net *net)
555 {
556         unsigned short *wdata;
557         unsigned long aligned_address;
558
559         aligned_address = (unsigned long)&net->data[ETHER_HDR_SIZE + IP_HDR_SIZE + net->align_offset];
560
561         wdata = (unsigned short *)aligned_address;
562         wdata[0] = htons(EDBG_CMD_WRITE_ACK);
563         wdata[1] = htons(net->blockNum);
564
565         net->dataLen = 4;
566
567         return ce_send_frame(net);
568 }
569
570 static int ce_process_download(ce_net *net, ce_bin *bin)
571 {
572         int ret = CE_PR_MORE;
573
574         if (net->dataLen >= 2) {
575                 unsigned short command;
576
577                 command = ntohs(*(unsigned short *)&net->data[CE_DOFFSET]);
578                 debug("command found: 0x%04X\n", command);
579
580                 switch (command) {
581                 case EDBG_CMD_WRITE_REQ:
582                         if (!net->link) {
583                                 // Check file name for WRITE request
584                                 // CE EShell uses "boot.bin" file name
585 #if 0
586                                 printf(">>>>>>>> First Frame, IP: %s, port: %d\n",
587                                         inet_ntoa((in_addr_t *)&net->srvAddrRecv),
588                                         ntohs(net->srvAddrRecv.sin_port));
589 #endif
590                                 if (strncmp((char *)&net->data[CE_DOFFSET + 2],
591                                                 "boot.bin", 8) == 0) {
592                                         // Some diag output
593                                         if (net->verbose) {
594                                                 printf("Locked Down download link, IP: ");
595                                                 print_IPaddr(net->srvAddrRecv.sin_addr);
596                                                 printf(", port: %d\n", ntohs(net->srvAddrRecv.sin_port));
597
598                                                 printf("Sending BOOTME request [%d] to ",
599                                                         net->seqNum);
600                                                 print_IPaddr(net->srvAddrSend.sin_addr);
601                                                 printf("\n");
602                                         }
603
604                                         // Lock down EShell download link
605                                         net->locAddr.sin_port = htons(EDBG_DOWNLOAD_PORT + 1);
606                                         net->srvAddrSend.sin_port = net->srvAddrRecv.sin_port;
607                                         net->srvAddrSend.sin_addr = net->srvAddrRecv.sin_addr;
608                                         net->link = 1;
609                                 } else {
610                                         // Unknown link
611                                         net->srvAddrRecv.sin_port = 0;
612                                 }
613
614                                 if (net->link) {
615                                         ce_send_write_ack(net);
616                                 }
617                         }
618                         break;
619
620                 case EDBG_CMD_WRITE:
621                         /* Fix data len */
622                         bin->dataLen = net->dataLen - 4;
623
624                         ret = ce_parse_bin(bin);
625                         if (ret != CE_PR_ERROR) {
626                                 net->blockNum++;
627                                 ce_send_write_ack(net);
628                         }
629                         break;
630
631                 case EDBG_CMD_READ_REQ:
632                         /* Read requests are not supported
633                          * Do nothing ...
634                          */
635                         break;
636
637                 case EDBG_CMD_ERROR:
638                         printf("Error: unknown error on the host side\n");
639
640                         bin->binLen = 0;
641                         ret = CE_PR_ERROR;
642                         break;
643
644                 default:
645                         printf("unknown command 0x%04X\n", command);
646                         return -EINVAL;
647                 }
648         }
649         return ret;
650 }
651
652 static void ce_init_edbg_link(ce_net *net)
653 {
654         /* Initialize EDBG link for commands */
655
656         net->locAddr.sin_port = htons(EDBG_DOWNLOAD_PORT);
657         net->srvAddrSend.sin_port = htons(EDBG_DOWNLOAD_PORT);
658         net->srvAddrRecv.sin_port = 0;
659         net->link = 0;
660 }
661
662 static void ce_process_edbg(ce_net *net, ce_bin *bin)
663 {
664         eth_dbg_hdr *header;
665
666         if (net->dataLen < sizeof(eth_dbg_hdr)) {
667                 /* Bad packet */
668
669                 net->srvAddrRecv.sin_port = 0;
670                 return;
671         }
672
673         header = (eth_dbg_hdr *)&net->data[net->align_offset + ETHER_HDR_SIZE + IP_HDR_SIZE];
674
675         if (header->id != EDBG_ID) {
676                 /* Bad packet */
677
678                 net->srvAddrRecv.sin_port = 0;
679                 return;
680         }
681
682         if (header->service != EDBG_SVC_ADMIN) {
683                 /* Unknown service */
684                 return;
685         }
686
687         if (!net->link) {
688                 /* Some diag output */
689                 if (net->verbose) {
690                         printf("Locked Down EDBG service link, IP: ");
691                         print_IPaddr(net->srvAddrRecv.sin_addr);
692                         printf(", port: %d\n", ntohs(net->srvAddrRecv.sin_port));
693                 }
694
695                 /* Lock down EDBG link */
696                 net->srvAddrSend.sin_port = net->srvAddrRecv.sin_port;
697                 net->link = 1;
698         }
699
700         switch (header->cmd) {
701         case EDBG_CMD_JUMPIMG:
702                 net->gotJumpingRequest = 1;
703
704                 if (net->verbose) {
705                         printf("Received JUMPING command\n");
706                 }
707                 /* Just pass through and copy CONFIG structure */
708         case EDBG_CMD_OS_CONFIG:
709                 /* Copy config structure */
710                 memcpy(&bin->edbgConfig, header->data,
711                         sizeof(edbg_os_config_data));
712                 if (net->verbose) {
713                         printf("Received CONFIG command\n");
714                         if (bin->edbgConfig.flags & EDBG_FL_DBGMSG) {
715                                 printf("--> Enabling DBGMSG service, IP: %d.%d.%d.%d, port: %d\n",
716                                         (bin->edbgConfig.dbgMsgIPAddr >> 0) & 0xFF,
717                                         (bin->edbgConfig.dbgMsgIPAddr >> 8) & 0xFF,
718                                         (bin->edbgConfig.dbgMsgIPAddr >> 16) & 0xFF,
719                                         (bin->edbgConfig.dbgMsgIPAddr >> 24) & 0xFF,
720                                         ntohs(bin->edbgConfig.dbgMsgPort));
721                         }
722
723                         if (bin->edbgConfig.flags & EDBG_FL_PPSH) {
724                                 printf("--> Enabling PPSH service, IP: %d.%d.%d.%d, port: %d\n",
725                                         (bin->edbgConfig.ppshIPAddr >> 0) & 0xFF,
726                                         (bin->edbgConfig.ppshIPAddr >> 8) & 0xFF,
727                                         (bin->edbgConfig.ppshIPAddr >> 16) & 0xFF,
728                                         (bin->edbgConfig.ppshIPAddr >> 24) & 0xFF,
729                                         ntohs(bin->edbgConfig.ppshPort));
730                         }
731
732                         if (bin->edbgConfig.flags & EDBG_FL_KDBG) {
733                                 printf("--> Enabling KDBG service, IP: %d.%d.%d.%d, port: %d\n",
734                                         (bin->edbgConfig.kdbgIPAddr >> 0) & 0xFF,
735                                         (bin->edbgConfig.kdbgIPAddr >> 8) & 0xFF,
736                                         (bin->edbgConfig.kdbgIPAddr >> 16) & 0xFF,
737                                         (bin->edbgConfig.kdbgIPAddr >> 24) & 0xFF,
738                                         ntohs(bin->edbgConfig.kdbgPort));
739                         }
740
741                         if (bin->edbgConfig.flags & EDBG_FL_CLEANBOOT) {
742                                 printf("--> Force clean boot\n");
743                         }
744                 }
745                 break;
746
747         default:
748                 if (net->verbose) {
749                         printf("Received unknown command: %08X\n", header->cmd);
750                 }
751                 return;
752         }
753
754         /* Respond with ack */
755         header->flags = EDBG_FL_FROM_DEV | EDBG_FL_ACK;
756         net->dataLen = EDBG_DATA_OFFSET;
757         ce_send_frame(net);
758 }
759
760 static int ce_send_bootme(ce_net *net)
761 {
762         eth_dbg_hdr *header;
763         edbg_bootme_data *data;
764 #ifdef DEBUG
765         int     i;
766         unsigned char   *pkt;
767 #endif
768         /* Fill out BOOTME packet */
769         memset(net->data, 0, PKTSIZE);
770         header = (eth_dbg_hdr *)&net->data[CE_DOFFSET];
771         data = (edbg_bootme_data *)header->data;
772
773         header->id = EDBG_ID;
774         header->service = EDBG_SVC_ADMIN;
775         header->flags = EDBG_FL_FROM_DEV;
776         header->seqNum = net->seqNum++;
777         header->cmd = EDBG_CMD_BOOTME;
778
779         data->versionMajor = 0;
780         data->versionMinor = 0;
781         data->cpuId = EDBG_CPU_TYPE_ARM;
782         data->bootmeVer = EDBG_CURRENT_BOOTME_VERSION;
783         data->bootFlags = 0;
784         data->downloadPort = 0;
785         data->svcPort = 0;
786
787         /* MAC address from environment*/
788         if (!eth_getenv_enetaddr("ethaddr", data->macAddr)) {
789                 printf("'ethaddr' is not set or invalid\n");
790                 memset(data->macAddr, 0, sizeof(data->macAddr));
791         }
792
793         /* IP address from environment */
794         data->ipAddr = getenv_IPaddr("ipaddr");
795
796         // Device name string (NULL terminated). Should include
797         // platform and number based on Ether address (e.g. Odo42, CEPCLS2346, etc)
798
799         // We will use lower MAC address segment to create device name
800         // eg. MAC '00-0C-C6-69-09-05', device name 'Triton05'
801
802         strncpy(data->platformId, "Triton", sizeof(data->platformId));
803         snprintf(data->deviceName, sizeof(data->deviceName), "%s%02X",
804                 data->platformId, data->macAddr[5]);
805
806 #ifdef DEBUG
807         printf("header->id: %08X\r\n", header->id);
808         printf("header->service: %08X\r\n", header->service);
809         printf("header->flags: %08X\r\n", header->flags);
810         printf("header->seqNum: %08X\r\n", header->seqNum);
811         printf("header->cmd: %08X\r\n\r\n", header->cmd);
812
813         printf("data->versionMajor: %08X\r\n", data->versionMajor);
814         printf("data->versionMinor: %08X\r\n", data->versionMinor);
815         printf("data->cpuId: %08X\r\n", data->cpuId);
816         printf("data->bootmeVer: %08X\r\n", data->bootmeVer);
817         printf("data->bootFlags: %08X\r\n", data->bootFlags);
818         printf("data->svcPort: %08X\r\n\r\n", ntohs(data->svcPort));
819
820         printf("data->macAddr: %02X-%02X-%02X-%02X-%02X-%02X\r\n",
821                 data->macAddr[0], data->macAddr[1],
822                 data->macAddr[2], data->macAddr[3],
823                 data->macAddr[4], data->macAddr[5]);
824
825         printf("data->ipAddr: %d.%d.%d.%d\r\n",
826                 (data->ipAddr >> 0) & 0xFF,
827                 (data->ipAddr >> 8) & 0xFF,
828                 (data->ipAddr >> 16) & 0xFF,
829                 (data->ipAddr >> 24) & 0xFF);
830
831         printf("data->platformId: %s\r\n", data->platformId);
832
833         printf("data->deviceName: %s\r\n", data->deviceName);
834 #endif
835         // Some diag output ...
836         if (net->verbose) {
837                 printf("Sending BOOTME request [%d] to ", net->seqNum);
838                 print_IPaddr(net->srvAddrSend.sin_addr);
839                 printf("\n");
840         }
841
842         net->dataLen = BOOTME_PKT_SIZE;
843 #ifdef DEBUG
844         debug("Start of buffer:      %p\n", net->data);
845         debug("Start of ethernet buffer:   %p\n", &net->data[net->align_offset]);
846         debug("Start of CE header:         %p\n", header);
847         debug("Start of CE data:           %p\n", data);
848
849         pkt = &net->data[net->align_offset];
850         debug("packet to send (ceconnect): \n");
851         for (i = 0; i < net->dataLen + ETHER_HDR_SIZE + IP_HDR_SIZE; i++) {
852                 debug("0x%02X ", pkt[i]);
853                 if (!((i + 1) % 16))
854                         debug("\n");
855         }
856         debug("\n");
857 #endif
858         memcpy(NetServerEther, NetBcastAddr, 6);
859         return ce_send_frame(net);
860 }
861
862 static void ce_init_download_link(ce_net *net, ce_bin *bin,
863                                 struct sockaddr_in *host_addr, int verbose)
864 {
865         unsigned long aligned_address;
866
867         /* Initialize EDBG link for download */
868         memset(net, 0, sizeof(*net));
869
870         /* our buffer contains space for ethernet- ip- and udp- headers */
871         /* calculate an offset so that our ce field is aligned to 4 bytes */
872         aligned_address = (unsigned long)net->data;
873         /* we need 42 bytes room for headers (14 Ethernet , 20 IPv4, 8 UDP) */
874         aligned_address += ETHER_HDR_SIZE + IP_HDR_SIZE;
875         /* want CE header aligned to 4 Byte boundary */
876         net->align_offset = (4 - (aligned_address % 4)) % 4;
877
878         net->locAddr.sin_family = AF_INET;
879         net->locAddr.sin_addr = getenv_IPaddr("ipaddr");
880         net->locAddr.sin_port = htons(EDBG_DOWNLOAD_PORT);
881
882         net->srvAddrSend.sin_family = AF_INET;
883         net->srvAddrSend.sin_port = htons(EDBG_DOWNLOAD_PORT);
884
885         net->srvAddrRecv.sin_family = AF_INET;
886         net->srvAddrRecv.sin_port = 0;
887
888         if (host_addr->sin_addr) {
889                 /* Use specified host address ... */
890                 net->srvAddrSend.sin_addr = host_addr->sin_addr;
891                 net->srvAddrRecv.sin_addr = host_addr->sin_addr;
892         } else {
893                 /* ... or default server address */
894                 net->srvAddrSend.sin_addr = getenv_IPaddr("serverip");
895                 net->srvAddrRecv.sin_addr = getenv_IPaddr("serverip");
896         }
897
898         net->verbose = verbose;
899
900         ce_init_bin(bin, &net->data[CE_DOFFSET + 4]);
901
902         eth_halt();
903
904 #ifdef CONFIG_NET_MULTI
905         eth_set_current();
906 #endif
907         if (eth_init(gd->bd) < 0) {
908 #ifdef ET_DEBUG
909                 puts("ceconnect: failed to init ethernet !\n");
910 #endif
911                 eth_halt();
912                 return;
913         }
914 #ifdef ET_DEBUG
915         puts("ceconnect: init ethernet done!\n");
916 #endif
917         memcpy(NetOurEther, eth_get_dev()->enetaddr, ETH_ALEN);
918         NetCopyIP(&NetOurIP, &gd->bd->bi_ip_addr);
919         NetOurGatewayIP = getenv_IPaddr("gatewayip");
920         NetOurSubnetMask = getenv_IPaddr("netmask");
921         NetServerIP = getenv_IPaddr("serverip");
922 }
923
924 static int do_ceconnect(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
925 {
926         int i;
927         int verbose = 0, use_timeout = 0;
928         int timeout = 0, recv_timeout, ret;
929         struct sockaddr_in host_ip_addr;
930
931         for (i = 1; i < argc; i++){
932                 if (*argv[i] != '-')
933                         break;
934                 if (argv[i][1] == 'v') {
935                         verbose = 1;
936                 } else if (argv[i][1] == 't') {
937                         i++;
938                         if (argc > i) {
939                                 timeout = simple_strtoul(argv[i],
940                                                         NULL, 10);
941                                 use_timeout = 1;
942                         } else {
943                                 printf("Option requires an argument - t\n");
944                                 return 1;
945                         }
946                 }
947         }
948
949         memset(&host_ip_addr, 0xff, sizeof(host_ip_addr));
950
951         ce_init_download_link(&g_net, &g_bin, &host_ip_addr, verbose);
952         while (1) {
953                 if (g_net.link) {
954                         recv_timeout = 3;
955                 } else {
956                         recv_timeout = 1;
957
958                         if (use_timeout && timeout <= 0) {
959                                 printf("CELOAD - Canceled, timeout\n");
960                                 eth_halt();
961                                 return 1;
962                         }
963                         if (ctrlc()) {
964                                 printf("CELOAD - canceled by user\n");
965                                 eth_halt();
966                                 return 1;
967                         }
968
969                         debug("sending broadcast frame bootme\n");
970
971                         if (ce_send_bootme(&g_net)) {
972                                 printf("CELOAD - error while sending BOOTME request\n");
973                                 eth_halt();
974                                 return 1;
975                         }
976                         debug("net state is: %d\n", NetState);
977                         if (verbose) {
978                                 if (use_timeout) {
979                                         printf("Waiting for connection, timeout %d sec\n", timeout);
980                                 } else {
981                                         printf("Waiting for connection, enter ^C to abort\n");
982                                 }
983                         }
984                 }
985
986                 if (ce_recv_frame(&g_net, recv_timeout)) {
987                         ret = ce_process_download(&g_net, &g_bin);
988                         if (ret != CE_PR_MORE)
989                                 break;
990                 } else if (use_timeout) {
991                         timeout -= recv_timeout;
992                 }
993         }
994
995         if (g_bin.binLen) {
996                 // Try to receive edbg commands from host
997                 ce_init_edbg_link(&g_net);
998                 if (verbose)
999                         printf("Waiting for EDBG commands ...\n");
1000
1001                 while (ce_recv_frame(&g_net, 3))
1002                         ce_process_edbg(&g_net, &g_bin);
1003
1004                 // Prepare WinCE image for execution
1005                 ce_prepare_run_bin(&g_bin);
1006
1007                 // Launch WinCE, if necessary
1008                 if (g_net.gotJumpingRequest)
1009                         ce_run_bin(&g_bin);
1010         }
1011         eth_halt();
1012         return 0;
1013 }
1014
1015 U_BOOT_CMD(
1016         ceconnect,      4,      1,      do_ceconnect,
1017         "ceconnect    - Set up a connection to the CE host PC over TCP/IP and download the run-time image\n",
1018         "ceconnect [-v] [-t <timeout>]\n"
1019         "  -v verbose operation\n"
1020         "  -t <timeout> - max wait time (#sec) for the connection\n"
1021 );