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