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