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