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