~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to roms/u-boot/tools/kwboot.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Boot a Marvell Kirkwood SoC, with Xmodem over UART0.
 
3
 *
 
4
 * (c) 2012 Daniel Stodden <daniel.stodden@gmail.com>
 
5
 *
 
6
 * References: marvell.com, "88F6180, 88F6190, 88F6192, and 88F6281
 
7
 *   Integrated Controller: Functional Specifications" December 2,
 
8
 *   2008. Chapter 24.2 "BootROM Firmware".
 
9
 */
 
10
 
 
11
#include <stdlib.h>
 
12
#include <stdio.h>
 
13
#include <string.h>
 
14
#include <stdarg.h>
 
15
#include <libgen.h>
 
16
#include <fcntl.h>
 
17
#include <errno.h>
 
18
#include <unistd.h>
 
19
#include <stdint.h>
 
20
#include <termios.h>
 
21
#include <sys/mman.h>
 
22
#include <sys/stat.h>
 
23
 
 
24
#include "kwbimage.h"
 
25
 
 
26
#ifdef __GNUC__
 
27
#define PACKED __attribute((packed))
 
28
#else
 
29
#define PACKED
 
30
#endif
 
31
 
 
32
/*
 
33
 * Marvell BootROM UART Sensing
 
34
 */
 
35
 
 
36
static unsigned char kwboot_msg_boot[] = {
 
37
        0xBB, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
 
38
};
 
39
 
 
40
#define KWBOOT_MSG_REQ_DELAY    10 /* ms */
 
41
#define KWBOOT_MSG_RSP_TIMEO    50 /* ms */
 
42
 
 
43
/*
 
44
 * Xmodem Transfers
 
45
 */
 
46
 
 
47
#define SOH     1       /* sender start of block header */
 
48
#define EOT     4       /* sender end of block transfer */
 
49
#define ACK     6       /* target block ack */
 
50
#define NAK     21      /* target block negative ack */
 
51
#define CAN     24      /* target/sender transfer cancellation */
 
52
 
 
53
struct kwboot_block {
 
54
        uint8_t soh;
 
55
        uint8_t pnum;
 
56
        uint8_t _pnum;
 
57
        uint8_t data[128];
 
58
        uint8_t csum;
 
59
} PACKED;
 
60
 
 
61
#define KWBOOT_BLK_RSP_TIMEO 1000 /* ms */
 
62
 
 
63
static int kwboot_verbose;
 
64
 
 
65
static void
 
66
kwboot_printv(const char *fmt, ...)
 
67
{
 
68
        va_list ap;
 
69
 
 
70
        if (kwboot_verbose) {
 
71
                va_start(ap, fmt);
 
72
                vprintf(fmt, ap);
 
73
                va_end(ap);
 
74
                fflush(stdout);
 
75
        }
 
76
}
 
77
 
 
78
static void
 
79
__spinner(void)
 
80
{
 
81
        const char seq[] = { '-', '\\', '|', '/' };
 
82
        const int div = 8;
 
83
        static int state, bs;
 
84
 
 
85
        if (state % div == 0) {
 
86
                fputc(bs, stdout);
 
87
                fputc(seq[state / div % sizeof(seq)], stdout);
 
88
                fflush(stdout);
 
89
        }
 
90
 
 
91
        bs = '\b';
 
92
        state++;
 
93
}
 
94
 
 
95
static void
 
96
kwboot_spinner(void)
 
97
{
 
98
        if (kwboot_verbose)
 
99
                __spinner();
 
100
}
 
101
 
 
102
static void
 
103
__progress(int pct, char c)
 
104
{
 
105
        const int width = 70;
 
106
        static const char *nl = "";
 
107
        static int pos;
 
108
 
 
109
        if (pos % width == 0)
 
110
                printf("%s%3d %% [", nl, pct);
 
111
 
 
112
        fputc(c, stdout);
 
113
 
 
114
        nl = "]\n";
 
115
        pos++;
 
116
 
 
117
        if (pct == 100) {
 
118
                while (pos++ < width)
 
119
                        fputc(' ', stdout);
 
120
                fputs(nl, stdout);
 
121
        }
 
122
 
 
123
        fflush(stdout);
 
124
 
 
125
}
 
126
 
 
127
static void
 
128
kwboot_progress(int _pct, char c)
 
129
{
 
130
        static int pct;
 
131
 
 
132
        if (_pct != -1)
 
133
                pct = _pct;
 
134
 
 
135
        if (kwboot_verbose)
 
136
                __progress(pct, c);
 
137
}
 
138
 
 
139
static int
 
140
kwboot_tty_recv(int fd, void *buf, size_t len, int timeo)
 
141
{
 
142
        int rc, nfds;
 
143
        fd_set rfds;
 
144
        struct timeval tv;
 
145
        ssize_t n;
 
146
 
 
147
        rc = -1;
 
148
 
 
149
        FD_ZERO(&rfds);
 
150
        FD_SET(fd, &rfds);
 
151
 
 
152
        tv.tv_sec = 0;
 
153
        tv.tv_usec = timeo * 1000;
 
154
        if (tv.tv_usec > 1000000) {
 
155
                tv.tv_sec += tv.tv_usec / 1000000;
 
156
                tv.tv_usec %= 1000000;
 
157
        }
 
158
 
 
159
        do {
 
160
                nfds = select(fd + 1, &rfds, NULL, NULL, &tv);
 
161
                if (nfds < 0)
 
162
                        goto out;
 
163
                if (!nfds) {
 
164
                        errno = ETIMEDOUT;
 
165
                        goto out;
 
166
                }
 
167
 
 
168
                n = read(fd, buf, len);
 
169
                if (n < 0)
 
170
                        goto out;
 
171
 
 
172
                buf = (char *)buf + n;
 
173
                len -= n;
 
174
        } while (len > 0);
 
175
 
 
176
        rc = 0;
 
177
out:
 
178
        return rc;
 
179
}
 
180
 
 
181
static int
 
182
kwboot_tty_send(int fd, const void *buf, size_t len)
 
183
{
 
184
        int rc;
 
185
        ssize_t n;
 
186
 
 
187
        rc = -1;
 
188
 
 
189
        do {
 
190
                n = write(fd, buf, len);
 
191
                if (n < 0)
 
192
                        goto out;
 
193
 
 
194
                buf = (char *)buf + n;
 
195
                len -= n;
 
196
        } while (len > 0);
 
197
 
 
198
        rc = tcdrain(fd);
 
199
out:
 
200
        return rc;
 
201
}
 
202
 
 
203
static int
 
204
kwboot_tty_send_char(int fd, unsigned char c)
 
205
{
 
206
        return kwboot_tty_send(fd, &c, 1);
 
207
}
 
208
 
 
209
static speed_t
 
210
kwboot_tty_speed(int baudrate)
 
211
{
 
212
        switch (baudrate) {
 
213
        case 115200:
 
214
                return B115200;
 
215
        case 57600:
 
216
                return B57600;
 
217
        case 38400:
 
218
                return B38400;
 
219
        case 19200:
 
220
                return B19200;
 
221
        case 9600:
 
222
                return B9600;
 
223
        }
 
224
 
 
225
        return -1;
 
226
}
 
227
 
 
228
static int
 
229
kwboot_open_tty(const char *path, speed_t speed)
 
230
{
 
231
        int rc, fd;
 
232
        struct termios tio;
 
233
 
 
234
        rc = -1;
 
235
 
 
236
        fd = open(path, O_RDWR|O_NOCTTY|O_NDELAY);
 
237
        if (fd < 0)
 
238
                goto out;
 
239
 
 
240
        memset(&tio, 0, sizeof(tio));
 
241
 
 
242
        tio.c_iflag = 0;
 
243
        tio.c_cflag = CREAD|CLOCAL|CS8;
 
244
 
 
245
        tio.c_cc[VMIN] = 1;
 
246
        tio.c_cc[VTIME] = 10;
 
247
 
 
248
        cfsetospeed(&tio, speed);
 
249
        cfsetispeed(&tio, speed);
 
250
 
 
251
        rc = tcsetattr(fd, TCSANOW, &tio);
 
252
        if (rc)
 
253
                goto out;
 
254
 
 
255
        rc = fd;
 
256
out:
 
257
        if (rc < 0) {
 
258
                if (fd >= 0)
 
259
                        close(fd);
 
260
        }
 
261
 
 
262
        return rc;
 
263
}
 
264
 
 
265
static int
 
266
kwboot_bootmsg(int tty, void *msg)
 
267
{
 
268
        int rc;
 
269
        char c;
 
270
 
 
271
        kwboot_printv("Sending boot message. Please reboot the target...");
 
272
 
 
273
        do {
 
274
                rc = tcflush(tty, TCIOFLUSH);
 
275
                if (rc)
 
276
                        break;
 
277
 
 
278
                rc = kwboot_tty_send(tty, msg, 8);
 
279
                if (rc) {
 
280
                        usleep(KWBOOT_MSG_REQ_DELAY * 1000);
 
281
                        continue;
 
282
                }
 
283
 
 
284
                rc = kwboot_tty_recv(tty, &c, 1, KWBOOT_MSG_RSP_TIMEO);
 
285
 
 
286
                kwboot_spinner();
 
287
 
 
288
        } while (rc || c != NAK);
 
289
 
 
290
        kwboot_printv("\n");
 
291
 
 
292
        return rc;
 
293
}
 
294
 
 
295
static int
 
296
kwboot_xm_makeblock(struct kwboot_block *block, const void *data,
 
297
                    size_t size, int pnum)
 
298
{
 
299
        const size_t blksz = sizeof(block->data);
 
300
        size_t n;
 
301
        int i;
 
302
 
 
303
        block->pnum = pnum;
 
304
        block->_pnum = ~block->pnum;
 
305
 
 
306
        n = size < blksz ? size : blksz;
 
307
        memcpy(&block->data[0], data, n);
 
308
        memset(&block->data[n], 0, blksz - n);
 
309
 
 
310
        block->csum = 0;
 
311
        for (i = 0; i < n; i++)
 
312
                block->csum += block->data[i];
 
313
 
 
314
        return n;
 
315
}
 
316
 
 
317
static int
 
318
kwboot_xm_sendblock(int fd, struct kwboot_block *block)
 
319
{
 
320
        int rc, retries;
 
321
        char c;
 
322
 
 
323
        retries = 16;
 
324
        do {
 
325
                rc = kwboot_tty_send(fd, block, sizeof(*block));
 
326
                if (rc)
 
327
                        break;
 
328
 
 
329
                rc = kwboot_tty_recv(fd, &c, 1, KWBOOT_BLK_RSP_TIMEO);
 
330
                if (rc)
 
331
                        break;
 
332
 
 
333
                if (c != ACK)
 
334
                        kwboot_progress(-1, '+');
 
335
 
 
336
        } while (c == NAK && retries-- > 0);
 
337
 
 
338
        rc = -1;
 
339
 
 
340
        switch (c) {
 
341
        case ACK:
 
342
                rc = 0;
 
343
                break;
 
344
        case NAK:
 
345
                errno = EBADMSG;
 
346
                break;
 
347
        case CAN:
 
348
                errno = ECANCELED;
 
349
                break;
 
350
        default:
 
351
                errno = EPROTO;
 
352
                break;
 
353
        }
 
354
 
 
355
        return rc;
 
356
}
 
357
 
 
358
static int
 
359
kwboot_xmodem(int tty, const void *_data, size_t size)
 
360
{
 
361
        const uint8_t *data = _data;
 
362
        int rc, pnum, N, err;
 
363
 
 
364
        pnum = 1;
 
365
        N = 0;
 
366
 
 
367
        kwboot_printv("Sending boot image...\n");
 
368
 
 
369
        do {
 
370
                struct kwboot_block block;
 
371
                int n;
 
372
 
 
373
                n = kwboot_xm_makeblock(&block,
 
374
                                        data + N, size - N,
 
375
                                        pnum++);
 
376
                if (n < 0)
 
377
                        goto can;
 
378
 
 
379
                if (!n)
 
380
                        break;
 
381
 
 
382
                rc = kwboot_xm_sendblock(tty, &block);
 
383
                if (rc)
 
384
                        goto out;
 
385
 
 
386
                N += n;
 
387
                kwboot_progress(N * 100 / size, '.');
 
388
        } while (1);
 
389
 
 
390
        rc = kwboot_tty_send_char(tty, EOT);
 
391
 
 
392
out:
 
393
        return rc;
 
394
 
 
395
can:
 
396
        err = errno;
 
397
        kwboot_tty_send_char(tty, CAN);
 
398
        errno = err;
 
399
        goto out;
 
400
}
 
401
 
 
402
static int
 
403
kwboot_term_pipe(int in, int out, char *quit, int *s)
 
404
{
 
405
        ssize_t nin, nout;
 
406
        char _buf[128], *buf = _buf;
 
407
 
 
408
        nin = read(in, buf, sizeof(buf));
 
409
        if (nin < 0)
 
410
                return -1;
 
411
 
 
412
        if (quit) {
 
413
                int i;
 
414
 
 
415
                for (i = 0; i < nin; i++) {
 
416
                        if (*buf == quit[*s]) {
 
417
                                (*s)++;
 
418
                                if (!quit[*s])
 
419
                                        return 0;
 
420
                                buf++;
 
421
                                nin--;
 
422
                        } else
 
423
                                while (*s > 0) {
 
424
                                        nout = write(out, quit, *s);
 
425
                                        if (nout <= 0)
 
426
                                                return -1;
 
427
                                        (*s) -= nout;
 
428
                                }
 
429
                }
 
430
        }
 
431
 
 
432
        while (nin > 0) {
 
433
                nout = write(out, buf, nin);
 
434
                if (nout <= 0)
 
435
                        return -1;
 
436
                nin -= nout;
 
437
        }
 
438
 
 
439
        return 0;
 
440
}
 
441
 
 
442
static int
 
443
kwboot_terminal(int tty)
 
444
{
 
445
        int rc, in, s;
 
446
        char *quit = "\34c";
 
447
        struct termios otio, tio;
 
448
 
 
449
        rc = -1;
 
450
 
 
451
        in = STDIN_FILENO;
 
452
        if (isatty(in)) {
 
453
                rc = tcgetattr(in, &otio);
 
454
                if (!rc) {
 
455
                        tio = otio;
 
456
                        cfmakeraw(&tio);
 
457
                        rc = tcsetattr(in, TCSANOW, &tio);
 
458
                }
 
459
                if (rc) {
 
460
                        perror("tcsetattr");
 
461
                        goto out;
 
462
                }
 
463
 
 
464
                kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n",
 
465
                              quit[0]|0100, quit[1]);
 
466
        } else
 
467
                in = -1;
 
468
 
 
469
        rc = 0;
 
470
        s = 0;
 
471
 
 
472
        do {
 
473
                fd_set rfds;
 
474
                int nfds = 0;
 
475
 
 
476
                FD_SET(tty, &rfds);
 
477
                nfds = nfds < tty ? tty : nfds;
 
478
 
 
479
                if (in >= 0) {
 
480
                        FD_SET(in, &rfds);
 
481
                        nfds = nfds < in ? in : nfds;
 
482
                }
 
483
 
 
484
                nfds = select(nfds + 1, &rfds, NULL, NULL, NULL);
 
485
                if (nfds < 0)
 
486
                        break;
 
487
 
 
488
                if (FD_ISSET(tty, &rfds)) {
 
489
                        rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL);
 
490
                        if (rc)
 
491
                                break;
 
492
                }
 
493
 
 
494
                if (FD_ISSET(in, &rfds)) {
 
495
                        rc = kwboot_term_pipe(in, tty, quit, &s);
 
496
                        if (rc)
 
497
                                break;
 
498
                }
 
499
        } while (quit[s] != 0);
 
500
 
 
501
        tcsetattr(in, TCSANOW, &otio);
 
502
out:
 
503
        return rc;
 
504
}
 
505
 
 
506
static void *
 
507
kwboot_mmap_image(const char *path, size_t *size, int prot)
 
508
{
 
509
        int rc, fd, flags;
 
510
        struct stat st;
 
511
        void *img;
 
512
 
 
513
        rc = -1;
 
514
        fd = -1;
 
515
        img = NULL;
 
516
 
 
517
        fd = open(path, O_RDONLY);
 
518
        if (fd < 0)
 
519
                goto out;
 
520
 
 
521
        rc = fstat(fd, &st);
 
522
        if (rc)
 
523
                goto out;
 
524
 
 
525
        flags = (prot & PROT_WRITE) ? MAP_PRIVATE : MAP_SHARED;
 
526
 
 
527
        img = mmap(NULL, st.st_size, prot, flags, fd, 0);
 
528
        if (img == MAP_FAILED) {
 
529
                img = NULL;
 
530
                goto out;
 
531
        }
 
532
 
 
533
        rc = 0;
 
534
        *size = st.st_size;
 
535
out:
 
536
        if (rc && img) {
 
537
                munmap(img, st.st_size);
 
538
                img = NULL;
 
539
        }
 
540
        if (fd >= 0)
 
541
                close(fd);
 
542
 
 
543
        return img;
 
544
}
 
545
 
 
546
static uint8_t
 
547
kwboot_img_csum8(void *_data, size_t size)
 
548
{
 
549
        uint8_t *data = _data, csum;
 
550
 
 
551
        for (csum = 0; size-- > 0; data++)
 
552
                csum += *data;
 
553
 
 
554
        return csum;
 
555
}
 
556
 
 
557
static int
 
558
kwboot_img_patch_hdr(void *img, size_t size)
 
559
{
 
560
        int rc;
 
561
        bhr_t *hdr;
 
562
        uint8_t csum;
 
563
        const size_t hdrsz = sizeof(*hdr);
 
564
 
 
565
        rc = -1;
 
566
        hdr = img;
 
567
 
 
568
        if (size < hdrsz) {
 
569
                errno = EINVAL;
 
570
                goto out;
 
571
        }
 
572
 
 
573
        csum = kwboot_img_csum8(hdr, hdrsz) - hdr->checkSum;
 
574
        if (csum != hdr->checkSum) {
 
575
                errno = EINVAL;
 
576
                goto out;
 
577
        }
 
578
 
 
579
        if (hdr->blockid == IBR_HDR_UART_ID) {
 
580
                rc = 0;
 
581
                goto out;
 
582
        }
 
583
 
 
584
        hdr->blockid = IBR_HDR_UART_ID;
 
585
 
 
586
        hdr->nandeccmode = IBR_HDR_ECC_DISABLED;
 
587
        hdr->nandpagesize = 0;
 
588
 
 
589
        hdr->srcaddr = hdr->ext
 
590
                ? sizeof(struct kwb_header)
 
591
                : sizeof(*hdr);
 
592
 
 
593
        hdr->checkSum = kwboot_img_csum8(hdr, hdrsz) - csum;
 
594
 
 
595
        rc = 0;
 
596
out:
 
597
        return rc;
 
598
}
 
599
 
 
600
static void
 
601
kwboot_usage(FILE *stream, char *progname)
 
602
{
 
603
        fprintf(stream,
 
604
                "Usage: %s -b <image> [ -p ] [ -t ] "
 
605
                "[-B <baud> ] <TTY>\n", progname);
 
606
        fprintf(stream, "\n");
 
607
        fprintf(stream, "  -b <image>: boot <image>\n");
 
608
        fprintf(stream, "  -p: patch <image> to type 0x69 (uart boot)\n");
 
609
        fprintf(stream, "\n");
 
610
        fprintf(stream, "  -t: mini terminal\n");
 
611
        fprintf(stream, "\n");
 
612
        fprintf(stream, "  -B <baud>: set baud rate\n");
 
613
        fprintf(stream, "\n");
 
614
}
 
615
 
 
616
int
 
617
main(int argc, char **argv)
 
618
{
 
619
        const char *ttypath, *imgpath;
 
620
        int rv, rc, tty, term, prot, patch;
 
621
        void *bootmsg;
 
622
        void *img;
 
623
        size_t size;
 
624
        speed_t speed;
 
625
 
 
626
        rv = 1;
 
627
        tty = -1;
 
628
        bootmsg = NULL;
 
629
        imgpath = NULL;
 
630
        img = NULL;
 
631
        term = 0;
 
632
        patch = 0;
 
633
        size = 0;
 
634
        speed = B115200;
 
635
 
 
636
        kwboot_verbose = isatty(STDOUT_FILENO);
 
637
 
 
638
        do {
 
639
                int c = getopt(argc, argv, "hb:ptB:");
 
640
                if (c < 0)
 
641
                        break;
 
642
 
 
643
                switch (c) {
 
644
                case 'b':
 
645
                        bootmsg = kwboot_msg_boot;
 
646
                        imgpath = optarg;
 
647
                        break;
 
648
 
 
649
                case 'p':
 
650
                        patch = 1;
 
651
                        break;
 
652
 
 
653
                case 't':
 
654
                        term = 1;
 
655
                        break;
 
656
 
 
657
                case 'B':
 
658
                        speed = kwboot_tty_speed(atoi(optarg));
 
659
                        if (speed == -1)
 
660
                                goto usage;
 
661
                        break;
 
662
 
 
663
                case 'h':
 
664
                        rv = 0;
 
665
                default:
 
666
                        goto usage;
 
667
                }
 
668
        } while (1);
 
669
 
 
670
        if (!bootmsg && !term)
 
671
                goto usage;
 
672
 
 
673
        if (patch && !imgpath)
 
674
                goto usage;
 
675
 
 
676
        if (argc - optind < 1)
 
677
                goto usage;
 
678
 
 
679
        ttypath = argv[optind++];
 
680
 
 
681
        tty = kwboot_open_tty(ttypath, speed);
 
682
        if (tty < 0) {
 
683
                perror(ttypath);
 
684
                goto out;
 
685
        }
 
686
 
 
687
        if (imgpath) {
 
688
                prot = PROT_READ | (patch ? PROT_WRITE : 0);
 
689
 
 
690
                img = kwboot_mmap_image(imgpath, &size, prot);
 
691
                if (!img) {
 
692
                        perror(imgpath);
 
693
                        goto out;
 
694
                }
 
695
        }
 
696
 
 
697
        if (patch) {
 
698
                rc = kwboot_img_patch_hdr(img, size);
 
699
                if (rc) {
 
700
                        fprintf(stderr, "%s: Invalid image.\n", imgpath);
 
701
                        goto out;
 
702
                }
 
703
        }
 
704
 
 
705
        if (bootmsg) {
 
706
                rc = kwboot_bootmsg(tty, bootmsg);
 
707
                if (rc) {
 
708
                        perror("bootmsg");
 
709
                        goto out;
 
710
                }
 
711
        }
 
712
 
 
713
        if (img) {
 
714
                rc = kwboot_xmodem(tty, img, size);
 
715
                if (rc) {
 
716
                        perror("xmodem");
 
717
                        goto out;
 
718
                }
 
719
        }
 
720
 
 
721
        if (term) {
 
722
                rc = kwboot_terminal(tty);
 
723
                if (rc && !(errno == EINTR)) {
 
724
                        perror("terminal");
 
725
                        goto out;
 
726
                }
 
727
        }
 
728
 
 
729
        rv = 0;
 
730
out:
 
731
        if (tty >= 0)
 
732
                close(tty);
 
733
 
 
734
        if (img)
 
735
                munmap(img, size);
 
736
 
 
737
        return rv;
 
738
 
 
739
usage:
 
740
        kwboot_usage(rv ? stderr : stdout, basename(argv[0]));
 
741
        goto out;
 
742
}