forked from avrdudes/avrdude
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathurclock.c
2599 lines (2176 loc) · 100 KB
/
urclock.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* AVRDUDE - A Downloader/Uploader for AVR device programmers
* Copyright (C) 2022, Stefan Rueger <stefan.rueger@urclocks.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $Id$ */
/*
* The Urclock programmer
*
* - Reads/writes flash/EEPROM of boards directly via the MCU bootloader and a serial connection
* - Works best in tandem with the urboot bootloader, but can deal with optiboot and similar
* - Implements urprotocol, a communication protocol designed for small bootloader sizes
* - Supports vector bootloaders by patching relevant interrupt vectors during upload:
* + Vector bootloaders run on all devices, not only those with a dedicated boot section
* + Can be considerably smaller than the smallest dedicated boot sections of a part, eg,
* only 256 bytes for ATmega2560 with an otherwise smallest boot section of 1024 bytes
* - Checks sizes of applications so they don't overwrite the bootloader
* - Provides a 4-byte metadata interface for
* + Allowing applications to utilise unused flash in a similar fashion to EEPROM
* + Storing in top flash the file name and last-modified-date of the uploaded application
* + Displaying file name and date of the application that was last uploaded
*
* As an example, the urboot bootloader including EEPROM r/w for the popular ATmega328p is only 384
* bytes, which frees up 128 bytes. On an ATmega1284p the urboot bootloader without EEPROM r/w is
* only 256 bytes, freeing 786 bytes on that device. Urboot bootloaders can be configured to
* - Upload and download applications
* - Read and write EEPROM
* - Provide an application function that writes flash memory pages; as this function is located
* at FLASHEND-4+1, no linker information is needed for the application
* - Operate dual boot from external SPI flash memory in addition to EEPROM r/w at a slightly
* increased bootloader of 512 bytes for a range of devices from the small ATtiny167, via the
* popular ATmega328p to the mighty ATmega2560.
*
*
* Urprotocol (the gory details, see also https://github.com/stefanrueger/urboot)
*
* The **explicit communication** between an uploader/downloader program (*"the programmer"*) and
* the bootloader is driven by the programmer, which sends command sequences to the bootloader and
* evaluates their return sequences. A command sequence starts by a command byte, followed by its
* parameters, followed by an end-of-parameter byte UR_EOP. In return the bootloader sends a fixed
* byte UR_INSYNC to acknowledge the command, then executes it, possibly returning data, followed
* by sending a different fixed byte UR_OK.
*
* Although the UR_INSYNC and UR_OK are *fixed constants* for a particular bootloader, they *can
* vary* between bootloaders to indicate
* - Which MCU the bootloader sits on (using one of up to 2040 predefined different MCU IDs)
* - Whether or not the bootloader provides a paged read flash command
* - Whether or not the bootloader has implemented the chip erase command
* - Whether or not writing a memory page memory looks like programming NOR memory
* - Two more whether-or-not bits that are currently reserved
*
* As UR_INSYNC and UR_OK should always differ, there are 256*255 possible combinations, one of
* which is reserved for backward compatibility mode where UR_INSYNC and UR_OK coincide with the
* respective STK500v1 constants. This protocol definition enables the bootloader to pass to the
* programmer log2(256*255-1) bits = 15.994331... bits of configuration information without having
* to spend a single additional byte of bootloader code. Subtracting the 5 bits for the "whether or
* not" info leaves 10.994331... bits which allows 2040 ≈ 2**10.994331... MCU ids.
*
* **Parameters.** Paged EEPROM/flash access commands and page erase are the only commands that
* need parameters. In this case the parameters are the address, followed by the length of the
* block to read or write and, if needed, followed by the bytes to be written. As in STK500v1,
* addresses are given as little endian (low byte first) and length as big endian (high byte
* first). The address always is a byte address (unless in compability mode). It is a 16-bit
* address for MCUs that have 65536 bytes flash or less, and a 24-bit address for MCUs with larger
* flash. Zero-length reads or writes are not supported by the protocol. If the *flash* page size
* is 256 or less, then the length parameter is sent as one byte (where 0 means 256 bytes).
* Otherwise the length parameter is sent as two bytes (where 0 means 65536). Note, however, that
* the only valid length for the write flash page command is the MCU page size; also the *maximum*
* valid length for EEPROM writes is 256 or the *flash* page size, whichever is higher. EEPROM
* write page commands should never exceed the size of half of SRAM though. The other two (read)
* paged-access commands are free to request any length between 1 and 256, and 1 and 65536,
* respectively. However, the programmer must never ask for an address block that would access
* bytes outside the range of EEPROM or flash on the device. Whilst the number of parameter bytes
* differs between bootloaders, for a particular bootloader the address and length is given always
* in the same way. This means that the EEPROM address on an MCU with a large flash will be a
* 24-bit address even though the EEPROM might only have 8192 bytes. Even though the write flash
* page command only allows one length, and page erase does not need a page at all, it must always
* be specified. This is to simplify the bootloader effort to decode the programmer's commands.
*
*
* Urprotocol commands
*
* - **UR_GET_SYNC:** The bootloader does nothing except returning the two protocol bytes. Its
* purpose is to synchronise the programmer with the bootloader and to identify the type of
* bootloader and (some of) its properties. For synchronisation, the programmer should issue a
* number of UR_GET_SYNC commands until it receives consistent UR_INSYNC and UR_OK values.
* At this point the programmer knows whether or not to switch to backward compatibility mode
* using the STK500v1 protocol as in -c arduino, which MCU is to be programmed etc. It is
* advised the programmer sets its read timeout in the synchronisation phase to less than 100 ms
* when reading the bootloader reply to avoid triggering the bootloader's watchdog timer. It is
* also recommended that the input is "drained" after successfully reading two response bytes to
* ensure the response has not been brought about by an application program of the connected
* board before the board was reset into bootloader mode. This command can also be used
* periodically to prevent the bootloader from timing out.
*
* - **UR_PROG_PAGE_FL:** One flash page is written to the device. In the absence of a
* UR_CHIP_ERASE (see below), the bootloader is expected to program the flash page as atomic
* page erase, page load and page write. If the bootloader implements UR_CHIP_ERASE, it has the
* choice of erasing a flash page before programming it or not. In case the bootloader erases
* pages before writing them, the payload of the UR_PROG_PAGE is programmed exactly as is; the
* programmer should implement desired sub-page modifications by first reading the flash
* contents of the not-to-be-modified page parts to correctly pad the page payload. If the
* bootloader does not erase pages before writing them, effectively the payload is *and*ed to
* the existing contents of the page thereby exposing the physical property of the underlying
* NOR flash memory; sub-page modifications can be carried out by padding the page buffer
* payload with 0xff, as programming 0xff is a NOP for AVR NOR flash memories.
*
* - **UR_CHIP_ERASE** (optional): If implemented, the bootloader erases to 0xff all flash
* except itself. After issuing the chip erase request it is advised the programmer set its
* timeout for reading the next character to more time than the bootloader will need to erase
* flash to avoid the programmer resuming communication before the bootloader comes back from
* the chip erase. 20 s should be sufficient. If the bootloader does not implement chip erase
* then the programmer should ensure that flash is erased to 0xff by, eg, repeated
* UR_PROG_PAGE calls with 0xff-only contents or equivalent; this normally takes longer than
* bootloader chip erase but is otherwise functionally equivalent to a UR_CHIP_ERASE
* implementation in the bootloader. The protocol does not expect EEPROM to be erased in either
* case. However, when implementing UR_CHIP_ERASE the bootloader is free to read fuses to
* determine whether or not EEPROM should also be erased and erase EEPROM accordingly.
*
* - **UR_READ_PAGE_FL** (optional) returns n=length bytes of flash from the given address
*
* - **UR_READ_PAGE_EE** (optional) returns n=length bytes of EEPROM from the given address
*
* - **UR_PROG_PAGE_EE** (optional) writes n=length bytes to the EEPROM at the given address
*
* - **UR_PAGE_ERASE** (optional) erases to 0xff a page at the given address (length must be given
* but is ignored)
*
* - **UR_LEAVE_PROGMODE** (optional): some bootloaders reduce the Watchdog timeout so that the
* application is started faster after programming
*
* - **Any other command**, should behave like UR_GET_SYNC, ie, the bootloader returns
* UR_INSYNC and UR_OK.
*
*
* **Error handling.** It is generally considered an error if the programmer asks for not
* implemented functionality, as it knows after synchronisation how the bootloader is configured.
* Hence, the bootloader WDT should reset on request of an optional, not implemented command.
* Typically, the bootloader would need to save the payload of EEPROM/flash writes to SRAM; for
* security reasons the bootloader should trigger a WDT reset if an illegitimate length of a paged
* write could overwrite the stack (eg, a request for writing 256 bytes EEPROM on a part with only
* 256 bytes SRAM). A protocol error detected by the bootloader (failure to match UR_EOP) should
* lead to a WDT reset. Protocol errors detected by the programmer (not matching UR_INSYNC or
* UR_OK) should normally lead to a termination of programming attempts. Frame errors in serial
* communication should also lead to a WDT reset or termination of programming, respectively. The
* bootloader should protect itself from being overwritten through own page writes and page erases.
*
*
* **Implicit communication** of further bootloader properties happens through a small table
* located at the top of flash. Normally, the programmer can read this table after establishing the
* MCU id, and therefore the location of top flash of the part for which the bootloader was
* compiled. The 6-byte table contains (top to bottom):
* - Version number: one byte, minor version 0..7 in three lsb, major version 0..31 in the 5 msb
* - Capabilities byte detailing, eg, whether the bootloader supports EEPROM r/w, dual boot etc
* - Two-byte rjmp to a writepage(ram, flash) function or a ret opcode if not implemented
* - Number 1..127 of pages that the bootloader occupies
* - Vector number 1..127 used for the r/jmp to the application if it is a vector bootloader
* If the bootloader does not have read capabilities the user needs to supply necessary information
* such as the bootloader size to the programmer on the command line via -x extended parameters.
*
* **Backward compatibility mode.** When urprotocol after synchronisation with the bootloader
* settles on UR_INSYNC and UR_OK values that turn out to be the STK500v1 values of 0x14 and 0x10,
* this triggers a backward compatibility mode. In this instance the programmer behaves (almost)
* like the STK500v1 implementation in avrdude's arduino programmer, ie, it handles optiboot and
* legacy bootloaders gracefully: in particular, the programmer can issue STK_READ_SIGN and two
* STK_UNIVERSAL requests (load extended address and chip erase) that the bootloader must implement
* in the backward compatibility mode. All EEPROM/flash addresses are sent as two-byte word
* addresses *little* endian, all length arguments are two-byte *big* endian, etc. Unlike avrdude
* -c arduino the programmer for the urprotocol should not pass on get and set hardware parameter
* requests, enquire software and hardware versions etc, as these requests would be wasteful for
* the bootloader. Under the urprotocol, bootloaders should be assured they do not need to even
* provide code to ignore these requests, even if they operate in the backwards compatibility mode.
*
*
* **Limitations.** Urprotocol has only provisions for reading EEPROM and flash, for writing EEPROM
* and for writing flash other than the bootloader area. In particular, urprotocol has no
* provisions for reading other memories such as the signature (other than in backward
* compatibility mode), calibration bytes, locks or fuses, and neither for writing lock bytes. The
* protocol does not consider sub-page flash writes, which are shifted to the programmer. If the
* bootloader's flash write does *not* look like NOR programming *and* if the bootloader does *not*
* provide flash read, then sub-page modifications simply cannot be done. Installing a bootloader
* has security implications as it provides a means to modify flash thus weakening the Harvard
* architecture of AVR microprocessors. Even bootloader implementations that are hardened against
* prohibited address and length parameters have, out of necessity, somewhere a code sequence that
* manipulates flash memory. A flawed application might still give an attacker a way to call these
* code sequences, so be warned here be dragons.
*
*/
#include "ac_cfg.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdarg.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "avrdude.h"
#include "libavrdude.h"
#include "urclock.h"
#include "urclock_private.h"
#define urmax(a, b) ((a) > (b)? (a): (b))
#define urmin(a, b) ((a) < (b)? (a): (b))
static int ur_initstruct(const PROGRAMMER *pgm, const AVRPART *p);
static int ur_readEF(const PROGRAMMER *pgm, const AVRPART *p, uint8_t *buf, uint32_t addr, int len,
char memchr);
static int readUrclockID(const PROGRAMMER *pgm, const AVRPART *p, uint64_t *idp);
static int urclock_send(const PROGRAMMER *pgm, unsigned char *buf, size_t len);
static int urclock_recv(const PROGRAMMER *pgm, unsigned char *buf, size_t len);
static int urclock_cmd(const PROGRAMMER *pgm, const unsigned char *cmd, unsigned char *res);
static int urclock_paged_write(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *m,
unsigned int page_size, unsigned int addr, unsigned int n_bytes);
// Context of the programmer
typedef struct {
char desc[32]; // Text description of bootloader version and capabilities
bool urprotocol; // Bootloader uses the urboot modification of the STK500v1 protocol
uint8_t urfeatures; // Bootloader features (chip erase, can read flash, ...)
int STK_INSYNC, STK_OK; // Variable but fixed bootloader responses for urprotocol
struct {
uint8_t seen, stk_ok, stk_insync;
} gs; // Needed for urclock_getsync()
unsigned char ext_addr_byte; // Ext-addr byte for STK500v1 protocol and MCUs with > 128k
uPcore_t uP; // Info about the connected processor (copied from uP_table)
bool initialised; // Is this structure initialised?
bool bleepromrw; // Bootloader has EEPROM r/w support
bool emulate_ce; // Emulate chip erase when bootloader cannot and user wants it
bool done_ce; // Set when flash of chip has been erased after first write
int sync_silence; // Temporarily set during start of synchronisation
// Info needed about bootloader to patch, if needed, the reset vector and one other vector
int vblvectornum, // Vector bootloader vector number for jump to application op code
vbllevel, // 0=n/a, 1=patch externally, 2=bl patches, 3=bl patches & verifies
blurversion, // Octal byte 076 means v7.6 (minor version number is lowest 3 bit)
// Small numbers < 070 probably are optiboot major version number
bloptiversion, // Optiboot version as (major<<8) + minor
blguessed; // Guessed the bootloader from hash data
int boothigh; // 1: Bootloader sits in high flash; 0: low flash (UPDI parts)
int32_t blstart, blend; // Bootloader address range [blstart, blend] for write protection
int32_t pfstart, pfend; // Programmable flash address range [pfstart, pfend]
int idmchr; // Either 'E' or 'F' for the memory where the Urclock ID is located
int idaddr; // The address of the Urclock ID
int idlen; // Number 1..8 of Urclock ID bytes (location, see iddesc below)
int32_t storestart; // Store (ie, unused flash) start address, same as application size
int32_t storesize; // Store size
// Metadata for free flash memory to be used for store support
char filename[254]; // Filename of uploaded application, must be max 254 bytes incl nul
int16_t yyyy; // Date stamp of uploaded application file: 4 digit year,
int8_t mm, dd, hr, mn; // Month (1..12), day (1..31), hour (0..23) and minute (0..59)
uint8_t freeflash[3]; // 24-bit little endian number (storesize)
uint8_t mcode; // 255 = no metadata, 0 = only freeflash, 1 = freeflash + date,
// 2-254 = freeflash + date + that many bytes filename incl nul
/*
* Examples:
* blend-blstart+1 = bootloader size
* FLASHEND+1 = application size + freeflash + nmeta(mcode, flashsize) + bootloader size
* Note for "classic" parts the bootloader is in high flash: blend = FLASHEND
* blstart = application size + freeflash + nmeta(mcode, flashsize)
* For "modern" parts the bootloader is in low flash: blstart = 0
*/
// Extended parameters for Urclock
int showall, // Show all pieces of info for connected part and exit
showid, // ... Urclock ID
showdate, // ... last-modified date of last uploaded application
showfilename, // ... filename of last uploaded application
showapp, // ... application size
showstore, // ... store size
showmeta, // ... metadata size
showboot, // ... bootloader size
showversion, // ... bootloader version and capabilities
showvector, // ... vector bootloader level, vector number and name
showpart, // ... part for which bootloader was compiled
xbootsize, // Manual override for size of bootloader section
xvectornum, // ... for vector number (implies vbllevel = 1)
xeepromrw, // ... for EEPROM r/w capability
xemulate_ce, // ... for making avrdude emulate any chip erase
initstore, // Zap store when writing the application, ie, fill with 0xff
//@@@ copystore, // Copy over store as far as possible when writing the application
restore, // Restore a flash backup exactly as it is trimming the bootloader
nofilename, // Don't store application filename when writing the application
nodate, // Don't store application filename and no date either
nometadata, // Don't store any metadata at all (implies no store support)
delay, // Additional delay [ms] after resetting the board, can be negative
strict; // Use strict synchronisation protocol
char title[254]; // Use instead of filename for metadata - same size as filename
char iddesc[64]; // Location of Urclock ID, eg F.12324.6 or E.-4.4 (default E.257.6)
} Urclock_t;
// Use private programmer data as if they were a global structure ur
#define ur (*(Urclock_t *)(pgm->cookie))
#define Return(...) do { pmsg_error(__VA_ARGS__); msg_error("\n"); return -1; } while (0)
// Return how many bytes metadata are needed given the mcode byte just below bootloader
static int nmeta(int mcode, int flashsize) {
// The size of the structure that holds info about metadata (sits just below bootloader)
int nheader = 2*(flashsize > (1<<16)? 4: 2) + 1;
return mcode == 0xff? 0: // No metadata at all
mcode > 1? mcode+6+nheader: // Application filename, app date and structure for pgm store
mcode? 6+nheader: // Application date and structure describing pgm store
nheader; // Structure describing pgm store
}
// Given the MCU id return index in uP_table or -1 if not found
static int upidxmcuid(int mcuid) {
for(size_t i=0; i< sizeof uP_table/sizeof *uP_table; i++) {
if(mcuid == uP_table[i].mcuid)
return i;
}
return -1;
}
// Given three signature bytes return index in uP_table or -1 if not found
static int upidxsig(const uint8_t *sigs) {
for(size_t i=0; i< sizeof uP_table/sizeof *uP_table; i++) {
if(0 == memcmp(sigs, uP_table[i].sigs, sizeof uP_table->sigs))
return i;
}
return -1;
}
// Given the long name of a part return index in uP table or -1 if not found
static int upidxname(const char *name) {
for(size_t i=0; i < sizeof uP_table/sizeof *uP_table; i++)
if(0 == strcasecmp(name, uP_table[i].name))
return i;
return -1;
}
// Given sig bytes return number of matching indices in uP_table and create a list of names in p
static int upmatchingsig(uint8_t sigs[3], char *p, size_t n) {
int matching = 0;
uPcore_t up = {0, };
// Scan table for the given signature
for(size_t i=0; i < sizeof uP_table/sizeof *uP_table; i++) {
if(0 == memcmp(sigs, uP_table[i].sigs, sizeof uP_table->sigs)) {
if(matching == 0) { // First match, initialise uP information
matching = 1;
up = uP_table[i];
if(p) {
size_t len = strlen(uP_table[i].name);
if(n > len) {
strcpy(p, uP_table[i].name);
n -= len; p += len;
}
}
} else {
// Same signature, but are these chips materially different as far as urboot is concerned?
if( up.ninterrupts != uP_table[i].ninterrupts ||
up.pagesize != uP_table[i].pagesize ||
up.nboots != uP_table[i].nboots ||
up.bootsize != uP_table[i].bootsize ||
up.flashsize != uP_table[i].flashsize ||
up.flashoffset != uP_table[i].flashoffset ) {
matching++;
if(p) {
size_t len = 2 + strlen(uP_table[i].name);
if(n > len) {
strcpy(p, ", ");
strcpy(p+2, uP_table[i].name);
n -= len; p += len;
}
}
}
}
}
}
return matching;
}
// Need to know a bit about avr opcodes, in particular jmp and rjmp for patching vector table
#define ret_opcode 0x9508
// Is the opcode an rjmp, ie, a relative jump [-4094, 4096] bytes from opcode address?
static int isRjmp(uint16_t opcode) {
return (opcode & 0xf000) == 0xc000;
}
/*
* Map distances to [-flashsize/2, flashsize/2) for smaller devices. As rjmp can go +/- 4 kB, so
* smaller flash than 8k (eg, 4k) benefit from wrap around logic.
*/
static int rjmpdistwrap(int addis, int flashsize) {
int size = flashsize > 8182? 8192: flashsize;
if((size & (size-1)) == 0) { // Sanity check to assert size is a power of 2; will be true
addis &= size-1;
if(addis >= size/2)
addis -= size;
}
return addis;
}
// Compute from rjmp opcode the relative distance in bytes (rjmp address minus destination address)
static int dist_rjmp(uint16_t rjmp, int flashsize) {
int16_t dist;
dist = rjmp & 0xfff; // Signed 12-bit word distance
dist = (int16_t)(dist<<4)>>3; // Sign-extend and multiply by 2
return rjmpdistwrap(dist+2, flashsize); // Wraps around 0 (eg, in flashes smaller than 8k)
}
// rjmp opcode from byte distance; 0xcfff is an endless loop, 0xc000 is a nop
static uint16_t rjmp_opcode(int dist, int flashsize) {
dist = rjmpdistwrap(dist, flashsize);
return 0xc000 | (((dist >> 1) - 1) & 0x0fff);
}
// rjmp opcode from reset to bootloader start; same as above if bl start is in top half of flash
static uint16_t rjmp_bwd_blstart(int blstart, int flashsize) { // flashsize must be power of 2
return 0xc000 | (((uint16_t)((blstart-flashsize-2)/2)) & 0xfff); // Urboot uses this formula
}
// jmp opcode from byte address
static uint32_t jmp_opcode(int32_t addr) {
// jmp uses word address; hence, shift by that one extra bit more
return (((addr>>1) & 0xffff)<<16) | 0x940c | (((addr>>18) & 31)<<4) | (((addr>>17) & 1)<<0);
}
// Byte address from jmp opcode
static int addr_jmp(uint32_t jmp) {
int addr;
addr = jmp >> 16; // Low 16 bit of word address are in upper word of op code
addr |= (jmp & 1) << 16; // Add extra address bits from least significant bytes of op code
addr |= (jmp & 0x1f0) << (17-4);
addr <<= 1; // Convert to byte address
return addr;
}
// Is the instruction word the lower 16 bit part of a 32-bit instruction?
static int isop32(uint16_t opcode) {
return
(opcode & 0xfe0f) == 0x9200 || // sts
(opcode & 0xfe0f) == 0x9000 || // lds
(opcode & 0xfe0e) == 0x940c || // jmp
(opcode & 0xfe0e) == 0x940e; // call
}
// Is the instruction word the lower 16 bit part of a jmp instruction?
static int isJmp(uint16_t opcode) {
return (opcode & 0xfe0e) == 0x940c;
}
// Assemble little endian 32-bit word from buffer
static uint32_t buf2uint32(const unsigned char *buf) {
return buf[0] | buf[1]<<8 | buf[2]<<16 | buf[3]<<24;
}
// Assemble little endian 16-bit word from buffer
static uint16_t buf2uint16(const unsigned char *buf) {
return buf[0] | buf[1]<<8;
}
// Write little endian 32-bit word into buffer
void uint32tobuf(unsigned char *buf, uint32_t opcode32) {
buf[0] = opcode32;
buf[1] = opcode32>>8;
buf[2] = opcode32>>16;
buf[3] = opcode32>>24;
}
// Write little endian 16-bit word into buffer
void uint16tobuf(unsigned char *buf, uint16_t opcode16) {
buf[0] = opcode16;
buf[1] = opcode16>>8;
}
// Set filename/title and date for metadata
static void set_date_filename(const PROGRAMMER *pgm, const char *fname) {
const char *base;
struct stat b;
struct tm *t;
time_t when;
// Last modification date of file or, if unavailable, current time
when = fname && *fname && strcmp(fname, "-") && !stat(fname, &b)? b.st_mtime: time(NULL);
when += 30; // Round to minute
if((t=localtime(& when))) {
ur.yyyy = t->tm_year + 1900;
ur.mm = t->tm_mon+1;
ur.dd = t->tm_mday;
ur.hr = t->tm_hour;
ur.mn = t->tm_min;
}
// Compute basename of file unless title was set
if(*ur.title)
memcpy(ur.filename, ur.title, sizeof ur.filename);
else {
ur.filename[0] = 0;
if(fname && *fname) {
if((base=strrchr(fname, '/')))
base++;
#ifdef WIN32
else if((base=strrchr(fname, '\\')))
base++;
#endif
else
base = fname;
strncpy(ur.filename, base, sizeof ur.filename-1);
ur.filename[sizeof ur.filename-1] = 0;
}
}
}
// Put destination address of reset vector jmp or rjmp into addr, return -1 if not an r/jmp
static int reset2addr(const unsigned char *opcode, int vecsz, int flashsize, int *addrp) {
int op32, addr, rc = 0;
uint16_t op16;
op16 = buf2uint16(opcode); // First word of the jmp or the full rjmp
op32 = vecsz == 2? op16: buf2uint32(opcode);
if(vecsz == 4 && isJmp(op16)) {
addr = addr_jmp(op32); // Accept compiler's destination (do not normalise)
} else if(isRjmp(op16)) { // rjmp might be generated for larger parts, too
addr = dist_rjmp(op16, flashsize);
while(addr < 0) // If rjmp was backwards
addr += flashsize; // OK for small parts, likely(!) OK if flashsize is a power of 2
while(addr > flashsize) // Sanity (should not happen): rjmp jumps over FLASHEND
addr -= flashsize;
} else
rc = -1;
if(addrp && rc == 0)
*addrp = addr;
return rc;
}
// What reset looks like for vector bootloaders
static int set_reset(const PROGRAMMER *pgm, unsigned char *jmptoboot, int vecsz) {
// Small part or larger flash that is power or 2: urboot P reset vector protection uses this
if(vecsz == 2 || (ur.uP.flashsize & (ur.uP.flashsize-1)) == 0) {
uint16tobuf(jmptoboot, rjmp_bwd_blstart(ur.blstart, ur.uP.flashsize));
return 2;
}
uint32tobuf(jmptoboot, jmp_opcode(ur.blstart));
return 4;
}
// Called after the input file has been read for writing or verifying flash
static int urclock_flash_readhook(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *flm,
const char *fname, int size) {
int nmdata, maxsize, firstbeg, firstlen;
int vecsz = ur.uP.flashsize <= 8192? 2: 4; // Small parts use rjmp, large parts need 4-byte jmp
set_date_filename(pgm, fname);
// Record how extensive the metadata should be, given the command line options (default: all)
ur.mcode = ur.nometadata? 0xff: ur.nodate? 0: ur.nofilename? 1: strlen(ur.filename)+1;
nmdata = nmeta(ur.mcode, ur.uP.flashsize);
maxsize = ur.pfend+1;
// Compute begin and length of first contiguous block in input
for(firstbeg=0; firstbeg < size; firstbeg++)
if(flm->tags[firstbeg] & TAG_ALLOCATED)
break;
for(firstlen=0; firstbeg+firstlen < size; firstlen++)
if(!(flm->tags[firstbeg+firstlen] & TAG_ALLOCATED))
break;
pmsg_notice2("%s %04d.%02d.%02d %02d.%02d meta %d boot %d\n", ur.filename,
ur.yyyy, ur.mm, ur.dd, ur.hr, ur.mn, nmdata, ur.blend > ur.blstart? ur.blend-ur.blstart+1: 0);
// Force upload of exactly this file, no patching, no metadata update, just trim if too big
if(ur.restore) {
if(size > maxsize)
size = maxsize;
goto nopatch_nometa;
}
// Sanity: no patching and no metadata if bootloader location is unknown
if(ur.blend <= ur.blstart)
goto nopatch_nometa;
// Sanity check the bootloader position
if(ur.blstart < 0 || ur.blstart >= flm->size || ur.blend < 0 || ur.blend >= flm->size)
Return("bootloader [0x%04x, 0x%04x] outside flash [0, 0x%04x]",
ur.blstart, ur.blend, flm->size-1);
// Check size of uploded application and protect bootloader from being overwritten
if((ur.boothigh && size > ur.pfend+1) || (!ur.boothigh && firstbeg <= ur.blend))
Return("input [0x%04x, 0x%04x] overlaps bootloader [0x%04x, 0x%04x]",
firstbeg, size-1, ur.blstart, ur.blend);
if(nmdata >= nmeta(0, ur.uP.flashsize) && size > ur.pfend+1 - nmeta(0, ur.uP.flashsize))
Return("input [0x%04x, 0x%04x] overlaps metadata [0x%04x, 0x%04x], consider -xnometadata",
firstbeg, size-1, ur.pfend+1-nmeta(0, ur.uP.flashsize), ur.pfend);
if(nmdata >= nmeta(1, ur.uP.flashsize) && size > ur.pfend+1 - nmeta(1, ur.uP.flashsize))
Return("input [0x%04x, 0x%04x] overlaps metadata [0x%04x, 0x%04x], consider -xnodate",
firstbeg, size-1, ur.pfend+1-nmeta(1, ur.uP.flashsize), ur.pfend);
if(size > ur.pfend+1 - nmdata)
Return("input [0x%04x, 0x%04x] overlaps metadata [0x%04x, 0x%04x], consider -xnofilename",
firstbeg, size-1, ur.pfend+1-nmdata, ur.pfend);
if(!ur.boothigh)
goto nopatch;
bool llcode = firstbeg == 0 && firstlen > ur.uP.ninterrupts*vecsz; // Looks like code
bool llvectors = firstbeg == 0 && firstlen >= ur.uP.ninterrupts*vecsz; // Looks like vector table
for(int i=0; llvectors && i<ur.uP.ninterrupts*vecsz; i+=vecsz) {
uint16_t op16 = buf2uint16(flm->buf+i);
if(!isRjmp(op16) && !(vecsz == 4 && isJmp(op16)))
llvectors = 0;
}
if(llcode && !llvectors && ur.vblvectornum > 0 && ur.vbllevel)
pmsg_warning("not patching jmp to application as input does not start with a vector table\n");
// Patch vectors if input looks like code and it's a vector bootloader with known vector number
if(llcode && llvectors && ur.vblvectornum > 0 && ur.vbllevel) {
// From v7.5 patch all levels but for earlier and unknown versions only patch level 1
if(ur.blurversion >= 075 || ((ur.blurversion==0 || ur.blurversion >= 072) && ur.vbllevel==1)) {
uint16_t reset16;
int reset32, appstart, appvecloc;
appvecloc = ur.vblvectornum*vecsz; // Location of jump-to-application in vector table
reset16 = buf2uint16(flm->buf); // First reset word of to-be-uploaded application
reset32 = vecsz == 2? reset16: buf2uint32(flm->buf);
/*
* Compute where the application starts from the reset vector. The assumptions are that the
* - Vector table, and therefore the reset vector, resides at address zero
* - Compiler puts either a jmp or an rjmp at address zero
* - Compiler does not shorten the vector table if no or few interrupts are used
* - Compiler does not utilise unused interrupt vectors to place code there
* These are not necessarily true, but work for run-of-the-mill setups; the code below makes
* a reasonable effort to detect whether the assumptions are violated, so at least there is
* an error thrown if so.
*/
if(reset2addr(flm->buf, vecsz, flm->size, &appstart) < 0) {
pmsg_warning("not patching input as opcode word %04x at reset is not a%sjmp\n",
reset16, vecsz==2? "n r": " ");
goto nopatch;
}
// Only patch if appstart does not already point to the bootloader
if(appstart != ur.blstart) {
int vectorsend = vecsz*ur.vblvectornum;
if(appstart < vectorsend || appstart >= size) { // appstart should be in [vectorsend, size)
if(appstart != ur.blstart) {
pmsg_warning("not patching as reset opcode %0*x jumps to 0x%04x,\n",
vecsz*2, reset32, appstart);
imsg_warning("ie, outside code area [0x%04x, 0x%04x)\n",
vectorsend, size);
}
goto nopatch;
}
// OK, now have bootloader start and application start: patch
set_reset(pgm, flm->buf+0, vecsz);
if(vecsz == 4)
uint32tobuf(flm->buf+appvecloc, jmp_opcode(appstart));
else
uint16tobuf(flm->buf+appvecloc, rjmp_opcode(appstart - appvecloc, ur.uP.flashsize));
}
}
}
nopatch:
if(nmdata) {
int32_t nfree = ur.pfend+1 - size;
if(nfree >= nmdata) {
unsigned char *p = flm->buf + ur.pfend+1 - nmdata;
if(ur.mcode > 1) { // Save filename (ur.mcode cannot be 0xff b/c nmdata is non-zero)
memcpy(p, ur.filename, ur.mcode);
p += ur.mcode;
}
if(ur.mcode >= 1) { // Save date
*p++ = ur.yyyy;
*p++ = ur.yyyy>>8;
*p++ = ur.mm;
*p++ = ur.dd;
*p++ = ur.hr;
*p++ = ur.mn;
}
*p++ = size; // Save where the pgm store begins
*p++ = size >> 8;
if(ur.uP.flashsize > (1<<16)) {
*p++ = size >> 16;
*p++ = size >> 24;
}
nfree -= nmdata;
*p++ = nfree; // Save how much is free
*p++ = nfree >> 8;
if(ur.uP.flashsize > (1<<16)) {
*p++ = nfree >> 16;
*p++ = nfree >> 24;
}
*p++ = ur.mcode;
// Set tags so metadata get burned onto chip
memset(flm->tags + ur.pfend+1 - nmdata, TAG_ALLOCATED, nmdata);
if(ur.initstore) // Zap the pgm store
memset(flm->tags + size, TAG_ALLOCATED, nfree);
size = ur.pfend+1;
}
}
// Storing no metadata: put a 0xff byte just below bootloader
if(size < ur.pfend+1 && nmdata == 0) {
flm->buf[ur.pfend] = 0xff;
flm->tags[ur.pfend] = TAG_ALLOCATED;
size = ur.pfend+1;
}
nopatch_nometa:
// Delete metadata on device (if any) that's between new input and metadata
if(!ur.urprotocol || (ur.urfeatures & UB_READ_FLASH)) { // Flash readable?
uint8_t devmcode; // Metadata marker on the device
if(ur_readEF(pgm, p, &devmcode, ur.pfend, 1, 'F') == 0) {
int devnmeta=nmeta(devmcode, ur.uP.flashsize);
for(int addr=ur.pfend+1-devnmeta; addr < ur.pfend+1; addr++) {
if(addr >=0 && addr < flm->size && !(flm->tags[addr] & TAG_ALLOCATED)) {
flm->tags[addr] |= TAG_ALLOCATED;
flm->buf[addr] = 0xff;
}
}
}
}
// Emulate chip erase if bootloader unable to: mark all bytes for upload on first -U flash:w:...
if(ur.emulate_ce) {
for(int ai = 0; ai < maxsize; ai++)
flm->tags[ai] = TAG_ALLOCATED;
ur.emulate_ce = 0;
}
// Ensure that vector bootloaders have correct r/jmp at address 0
if(ur.boothigh && ur.blstart && ur.vbllevel == 1) {
int rc, set=0;
for(int i=0; i < vecsz; i++)
if(flm->tags[i] & TAG_ALLOCATED)
set++;
// Reset vector not programmed? Or -F? Ensure a jmp to bootloader
if(ovsigck || set != vecsz) {
unsigned char jmptoboot[4];
int resetsize = set_reset(pgm, jmptoboot, vecsz);
if(!ur.urprotocol || (ur.urfeatures & UB_READ_FLASH)) { // Flash readable?
int resetdest;
if(set != vecsz) {
unsigned char device[4];
// Read reset vector from device flash
if((rc = ur_readEF(pgm, p, device, 0, vecsz, 'F')) < 0)
return rc;
// Mix with already set bytes
for(int i=0; i < vecsz; i++)
if(!(flm->tags[i] & TAG_ALLOCATED))
flm->buf[i] = device[i];
}
if(reset2addr(flm->buf, vecsz, flm->size, &resetdest) < 0 || resetdest != ur.blstart) {
for(int i=0; i < resetsize; i++) {
flm->buf[i] = jmptoboot[i];
flm->tags[i] |= TAG_ALLOCATED;
}
}
} else { // Flash not readable: patch reset vector unconditionally
for(int i=0; i < resetsize; i++) {
flm->buf[i] = jmptoboot[i];
flm->tags[i] |= TAG_ALLOCATED;
}
}
} else { // Double-check reset vector jumps to bootloader
int resetdest;
if(reset2addr(flm->buf, vecsz, flm->size, &resetdest) < 0)
Return("input would overwrite the reset vector bricking the bootloader\n"
"%*susing -F will try to patch the input but this may not be what is needed",
(int) strlen(progname)+1, "");
if(resetdest != ur.blstart)
Return("input points reset to 0x%04x, not to bootloader at 0x%04x\n"
"%*susing -F will try to patch the input but this may not be what is needed",
resetdest, ur.blstart, (int) strlen(progname)+1, "");
}
}
// Effective page size, can be 4*pagesize for 4-page erase parts
int pgsize = p->n_page_erase > 0? p->n_page_erase*ur.uP.pagesize: ur.uP.pagesize;
if((pgsize & (pgsize-1)) || pgsize < 1 || pgsize > maxsize || maxsize % pgsize)
Return("effective page size %d implausible for size %d below bootloader", pgsize, maxsize);
if(!ur.done_ce) { // Unless chip erase was just issued (where all mem is 0xff)
if((ur.urprotocol && !(ur.urfeatures & UB_FLASH_LL_NOR)) || !ur.urprotocol) {
// Scan the memory for eff pages with unset bytes and read these bytes from device flash
int ai, npe, addr, nset;
uint8_t spc[2048];
for(addr = 0; addr < maxsize; addr += pgsize) {
// How many bytes are set in this effective page?
for(ai = addr, nset = 0; ai < addr + pgsize; ai++)
if(flm->tags[ai] & TAG_ALLOCATED)
nset++;
// Holes in this page that needs writing? read them in from the chip
if(nset && nset != pgsize) {
for(npe=0; npe < pgsize/ur.uP.pagesize; npe++) {
// Identify a covering interval for all holes in page
int istart, isize, beg, end;
beg = addr + npe*ur.uP.pagesize;
end = beg + ur.uP.pagesize;
// Lowest address with unset byte (there might be none)
for(ai = beg; ai < end; ai++)
if(!(flm->tags[ai] & TAG_ALLOCATED))
break;
istart = ai;
if(istart < end) {
// Highest address with unset byte
for(ai = end - 1; ai >= istart; ai--)
if(!(flm->tags[ai] & TAG_ALLOCATED))
break;
isize = ai - istart + 1;
if(isize < 1 || isize > (int) sizeof spc) // Should not happen
Return("isize=%d out of range (enlarge spc[] and recompile)", isize);
if(ur_readEF(pgm, p, spc, istart, isize, 'F') == 0) {
pmsg_debug("padding [0x%04x, 0x%04x]\n", istart, istart+isize-1);
for(ai = istart; ai < istart + isize; ai++)
if(!(flm->tags[ai] & TAG_ALLOCATED)) {
flm->tags[ai] |= TAG_ALLOCATED;
flm->buf[ai] = spc[ai-istart];
}
} else {
pmsg_notice2("cannot read flash [0x%04x, 0x%04x] to pad page bytes\n",
istart, istart+isize-1);
}
}
}
}
}
}
}
ur.done_ce = 0; // From now on can no longer rely on being deleted
// Fill remaining holes (chip was erased, could not be read or memory looks like NOR memory)
int ai, addr, nset;
for(addr = 0; addr < maxsize; addr += pgsize) {
for(ai = addr, nset = 0; ai < addr + pgsize; ai++)
if(flm->tags[ai] & TAG_ALLOCATED)
nset++;
if(nset && nset != pgsize) { // Page has holes: fill them
pmsg_debug("0xff padding page addr 0x%04d\n", addr);
for(ai = addr, nset = 0; ai < addr + pgsize; ai++)
if(!(flm->tags[ai] & TAG_ALLOCATED)) {
flm->tags[ai] |= TAG_ALLOCATED;
flm->buf[ai] = 0xff;
}
}
}
return size;
}
// Put version string into a buffer of max 19 characters incl nul (normally 15-16 bytes incl nul)
static void urbootPutVersion(const PROGRAMMER *pgm, char *buf, uint16_t ver, uint16_t rjmpwp) {
uint8_t hi = ver>>8, type = ver & 0xff, flags;
if(ver == 0xffff) // Unknown provenance
hi = type = 0;
if(hi >= 072) { // These are urboot versions
sprintf(buf, "u%d.%d ", hi>>3, hi&7);
buf += strlen(buf);
*buf++ = (hi < 077 && (type & UR_PGMWRITEPAGE)) || (hi >= 077 && rjmpwp != ret_opcode)? 'w': '-';
*buf++ = type & UR_EEPROM? 'e': '-';
if(hi >= 076) { // From urboot version 7.6 URPROTOCOL has its own bit
*buf++ = type & UR_URPROTOCOL? 'u': 's';
*buf++ = type & UR_DUAL? 'd': '-';
} else {
*buf++ = '-'; // Dummy bit
flags = (type/UR_DUAL) & 3;
// D = Dual boot with SE & SPI restoration, d = dual boot with SE, f = dual boot only
*buf++ = flags==3? 'D': flags==2? 'd': flags? 'f': '-';
}
flags = (type/UR_VBL) & 3;
// V = VBL, patch & verify, v = VBL, patch only, j = VBL, jump only
*buf++ = flags==3? 'V': flags==2? 'v': flags? 'j': 'h';
*buf++ = hi < 077? (type & UR_PROTECTME? 'p': '-'): (type & UR_PROTECTME? 'P': 'p');
*buf++ = (hi < 077 && (type & UR_RESETFLAGS)) || hi >= 077? 'r': '-';
*buf++ = hi >= 077 && (type & UR_AUTOBAUD)? 'a': '-'; // - means no
*buf++ = hi >= 077 && (type & UR_HAS_CE)? 'c': hi >= 077? '-': '.'; // . means don't know
*buf = 0;
} else if(hi) { // Version number in binary from optiboot v4.1
sprintf(buf, "o%d.%d -%cs-%c-r--", hi, type,
ur.blguessed? (ur.bleepromrw? 'e': '-'): '?',
ur.blguessed? "hjvV"[ur.vbllevel & 3]: '?');
} else
sprintf(buf, "x0.0 .........");
return;
}
// Return name of the vector with number num
static const char *vblvecname(const PROGRAMMER *pgm, int num) {
// This should never happen
if(num < -1 || num > ur.uP.ninterrupts || !ur.uP.isrtable)
return("unknown");