-
Notifications
You must be signed in to change notification settings - Fork 2
/
optiboot.c
1381 lines (1242 loc) · 45.6 KB
/
optiboot.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
#define FUNC_READ 1
#define FUNC_WRITE 1
/**********************************************************/
/* Optiboot bootloader for Arduino */
/* */
/* http://optiboot.googlecode.com */
/* */
/* Arduino-maintained version : See README.TXT */
/* http://code.google.com/p/arduino/ */
/* It is the intent that changes not relevant to the */
/* Arduino production envionment get moved from the */
/* optiboot project to the arduino project in "lumps." */
/* */
/* Heavily optimised bootloader that is faster and */
/* smaller than the Arduino standard bootloader */
/* */
/* Enhancements: */
/* Fits in 512 bytes, saving 1.5K of code space */
/* Higher baud rate speeds up programming */
/* Written almost entirely in C */
/* Customisable timeout with accurate timeconstant */
/* Optional virtual UART. No hardware UART required. */
/* Optional virtual boot partition for devices without. */
/* */
/* What you lose: */
/* Implements a skeleton STK500 protocol which is */
/* missing several features including EEPROM */
/* programming and non-page-aligned writes */
/* High baud rate breaks compatibility with standard */
/* Arduino flash settings */
/* */
/* Fully supported: */
/* ATmega168 based devices (Diecimila etc) */
/* ATmega328P based devices (Duemilanove etc) */
/* */
/* Beta test (believed working.) */
/* ATmega8 based devices (Arduino legacy) */
/* ATmega328 non-picopower devices */
/* ATmega644P based devices (Sanguino) */
/* ATmega1284P based devices */
/* ATmega1280 based devices (Arduino Mega) */
/* ATmega2560 based devices (Arduino Mega) */
/* */
/* Alpha test */
/* ATmega32 */
/* */
/* Work in progress: */
/* ATtiny84 based devices (Luminet) */
/* */
/* Does not support: */
/* USB based devices (eg. Teensy, Leonardo) */
/* */
/* Assumptions: */
/* The code makes several assumptions that reduce the */
/* code size. They are all true after a hardware reset, */
/* but may not be true if the bootloader is called by */
/* other means or on other hardware. */
/* No interrupts can occur */
/* UART and Timer 1 are set to their reset state */
/* SP points to RAMEND */
/* */
/* Code builds on code, libraries and optimisations from: */
/* stk500boot.c by Jason P. Kyle */
/* Arduino bootloader http://arduino.cc */
/* Spiff's 1K bootloader http://spiffie.org/know/arduino_1k_bootloader/bootloader.shtml */
/* avr-libc project http://nongnu.org/avr-libc */
/* Adaboot http://www.ladyada.net/library/arduino/bootloader.html */
/* AVR305 Atmel Application Note */
/* */
/* Copyright 2013-2015 by Bill Westfield. */
/* Copyright 2010 by Peter Knight. */
/* */
/* 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, write */
/* to the Free Software Foundation, Inc., */
/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* */
/* Licence can be viewed at */
/* http://www.fsf.org/licenses/gpl.txt */
/* */
/**********************************************************/
/**********************************************************/
/* */
/* Optional defines: */
/* */
/**********************************************************/
/* */
/* BIGBOOT: */
/* Build a 1k bootloader, not 512 bytes. This turns on */
/* extra functionality. */
/* */
/* BAUD_RATE: */
/* Set bootloader baud rate. */
/* */
/* SOFT_UART: */
/* Use AVR305 soft-UART instead of hardware UART. */
/* */
/* LED_START_FLASHES: */
/* Number of LED flashes on bootup. */
/* */
/* LED_DATA_FLASH: */
/* Flash LED when transferring data. For boards without */
/* TX or RX LEDs, or for people who like blinky lights. */
/* */
/* SUPPORT_EEPROM: */
/* Support reading and writing from EEPROM. This is not */
/* used by Arduino, so off by default. */
/* */
/* TIMEOUT_MS: */
/* Bootloader timeout period, in milliseconds. */
/* 500,1000,2000,4000,8000 supported. */
/* */
/* UART: */
/* UART number (0..n) for devices with more than */
/* one hardware uart (644P, 1284P, etc) */
/* */
/**********************************************************/
/**********************************************************/
/* Version Numbers! */
/* */
/* Arduino Optiboot now includes this Version number in */
/* the source and object code. */
/* */
/* Version 3 was released as zip from the optiboot */
/* repository and was distributed with Arduino 0022. */
/* Version 4 starts with the arduino repository commit */
/* that brought the arduino repository up-to-date with */
/* the optiboot source tree changes since v3. */
/* Version 5 was created at the time of the new Makefile */
/* structure (Mar, 2013), even though no binaries changed*/
/* Version 6 added EEPROM support, including causing an */
/* error when trying to write eeprom with versions that */
/* didn't have the code there. Makefiles were further */
/* restructured. Overlapping SPM/download removed. */
/* Version 7 straightened out the MCUSR and RESET */
/* handling, did MORE Makefile mods. EEPROM support now */
/* fits in 512 bytes, if you turn off LED Blinking. */
/* Various bigboot and virboot targets were fixed. */
/* Version 8.0 adds the do_spm code callable from Apps. */
/* */
/* It would be good if versions implemented outside the */
/* official repository used an out-of-seqeunce version */
/* number (like 104.6 if based on based on 4.5) to */
/* prevent collisions. The CUSTOM_VERSION=n option */
/* adds n to the high version to facilitate this. */
/* */
/**********************************************************/
/**********************************************************/
/* Edit History: */
/* */
/* Sep 2018 */
/* 8.0 WestfW (and Majekw and MCUDude) */
/* Include do_spm routine callable from the app */
/* at BOOTSTART+2, controllable with compile option */
/* July 2018 */
/* 7.0 WestfW (with much input from Others) */
/* Fix MCUSR treatement as per much discussion, */
/* Patches by MarkG55, majekw. Preserve value */
/* for the application, as much as possible. */
/* see https://github.com/Optiboot/optiboot/issues/97 */
/* Optimize a bit by implementing a union for the */
/* various 16bit address values used (based on */
/* observation by "aweatherguy", but different.) */
/* Slightly optimize math in VIRTUAL_BOOT code */
/* Add some virboot targets, fix some fuses. */
/* Implement LED_START_ON; less code than flashes */
/* Aug 2014 */
/* 6.2 WestfW: make size of length variables dependent */
/* on the SPM_PAGESIZE. This saves space */
/* on the chips where it's most important. */
/* 6.1 WestfW: Fix OPTIBOOT_CUSTOMVER (send it!) */
/* Make no-wait mod less picky about */
/* skipping the bootloader. */
/* Remove some dead code */
/* Jun 2014 */
/* 6.0 WestfW: Modularize memory read/write functions */
/* Remove serial/flash overlap */
/* (and all references to NRWWSTART/etc) */
/* Correctly handle pagesize > 255bytes */
/* Add EEPROM support in BIGBOOT (1284) */
/* EEPROM write on small chips now causes err */
/* Split Makefile into smaller pieces */
/* Add Wicked devices Wildfire */
/* Move UART=n conditionals into pin_defs.h */
/* Remove LUDICOUS_SPEED option */
/* Replace inline assembler for .version */
/* and add OPTIBOOT_CUSTOMVER for user code */
/* Fix LED value for Bobuino (Makefile) */
/* Make all functions explicitly inline or */
/* noinline, so we fit when using gcc4.8 */
/* Change optimization options for gcc4.8 */
/* Make ENV=arduino work in 1.5.x trees. */
/* May 2014 */
/* 5.0 WestfW: Add support for 1Mbps UART */
/* Mar 2013 */
/* 5.0 WestfW: Major Makefile restructuring. */
/* See Makefile and pin_defs.h */
/* (no binary changes) */
/* */
/* 4.6 WestfW/Pito: Add ATmega32 support */
/* 4.6 WestfW/radoni: Don't set LED_PIN as an output if */
/* not used. (LED_START_FLASHES = 0) */
/* Jan 2013 */
/* 4.6 WestfW/dkinzer: use autoincrement lpm for read */
/* 4.6 WestfW/dkinzer: pass reset cause to app in R2 */
/* Mar 2012 */
/* 4.5 WestfW: add infrastructure for non-zero UARTS. */
/* 4.5 WestfW: fix SIGNATURE_2 for m644 (bad in avr-libc) */
/* Jan 2012: */
/* 4.5 WestfW: fix NRWW value for m1284. */
/* 4.4 WestfW: use attribute OS_main instead of naked for */
/* main(). This allows optimizations that we */
/* count on, which are prohibited in naked */
/* functions due to PR42240. (keeps us less */
/* than 512 bytes when compiler is gcc4.5 */
/* (code from 4.3.2 remains the same.) */
/* 4.4 WestfW and Maniacbug: Add m1284 support. This */
/* does not change the 328 binary, so the */
/* version number didn't change either. (?) */
/* June 2011: */
/* 4.4 WestfW: remove automatic soft_uart detect (didn't */
/* know what it was doing or why.) Added a */
/* check of the calculated BRG value instead. */
/* Version stays 4.4; existing binaries are */
/* not changed. */
/* 4.4 WestfW: add initialization of address to keep */
/* the compiler happy. Change SC'ed targets. */
/* Return the SW version via READ PARAM */
/* 4.3 WestfW: catch framing errors in getch(), so that */
/* AVRISP works without HW kludges. */
/* http://code.google.com/p/arduino/issues/detail?id=368n*/
/* 4.2 WestfW: reduce code size, fix timeouts, change */
/* verifySpace to use WDT instead of appstart */
/* 4.1 WestfW: put version number in binary. */
/**********************************************************/
#define OPTIBOOT_MAJVER 8
#define OPTIBOOT_MINVER 0
/*
* OPTIBOOT_CUSTOMVER should be defined (by the makefile) for custom edits
* of optiboot. That way you don't wind up with very different code that
* matches the version number of a "released" optiboot.
*/
#if !defined(OPTIBOOT_CUSTOMVER)
#define OPTIBOOT_CUSTOMVER 0
#endif
unsigned const int __attribute__((section(".version")))
optiboot_version = 256*(OPTIBOOT_MAJVER + OPTIBOOT_CUSTOMVER) + OPTIBOOT_MINVER;
#include <inttypes.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <util/delay.h>
/*
* optiboot uses several "address" variables that are sometimes byte pointers,
* sometimes word pointers. sometimes 16bit quantities, and sometimes built
* up from 8bit input characters. avr-gcc is not great at optimizing the
* assembly of larger words from bytes, but we can use the usual union to
* do this manually. Expanding it a little, we can also get rid of casts.
*/
typedef union {
uint8_t *bptr;
uint16_t *wptr;
uint16_t word;
uint8_t bytes[2];
} addr16_t;
/*
* Note that we use a replacement of "boot.h"
* <avr/boot.h> uses sts instructions, but this version uses out instructions
* This saves cycles and program memory, if possible.
* boot_opt.h pulls in the standard boot.h for the odd target (?)
*/
#include "boot_opt.h"
// We don't use <avr/wdt.h> as those routines have interrupt overhead we don't need.
/*
* pin_defs.h
* This contains most of the rather ugly defines that implement our
* ability to use UART=n and LED=D3, and some avr family bit name differences.
*/
#include "pin_defs.h"
/*
* stk500.h contains the constant definitions for the stk500v1 comm protocol
*/
#include "stk500.h"
#include "avrio.h"
#include "pinout.h"
#ifndef LED_START_FLASHES
#define LED_START_FLASHES 0
#endif
/* set the UART baud rate defaults */
#ifndef BAUD_RATE
#if F_CPU >= 8000000L
#define BAUD_RATE 115200L // Highest rate Avrdude win32 will support
#elif F_CPU >= 1000000L
#define BAUD_RATE 9600L // 19200 also supported, but with significant error
#elif F_CPU >= 128000L
#define BAUD_RATE 4800L // Good for 128kHz internal RC
#else
#define BAUD_RATE 1200L // Good even at 32768Hz
#endif
#endif
#ifndef UART
#define UART 0
#endif
#ifndef SOFT_UART
#ifdef SINGLESPEED
/* Single speed option */
#define BAUD_SETTING (( (F_CPU + BAUD_RATE * 8L) / ((BAUD_RATE * 16L))) - 1 )
#define BAUD_ACTUAL (F_CPU/(16 * ((BAUD_SETTING)+1)))
#else
/* Normal U2X usage */
#define BAUD_SETTING (( (F_CPU + BAUD_RATE * 4L) / ((BAUD_RATE * 8L))) - 1 )
#define BAUD_ACTUAL (F_CPU/(8 * ((BAUD_SETTING)+1)))
#endif
#if BAUD_ACTUAL <= BAUD_RATE
#define BAUD_ERROR (( 100*(BAUD_RATE - BAUD_ACTUAL) ) / BAUD_RATE)
#if BAUD_ERROR >= 5
#error BAUD_RATE off by greater than -5%
#elif BAUD_ERROR >= 2 && !defined(PRODUCTION)
#warning BAUD_RATE off by greater than -2%
#endif
#else
#define BAUD_ERROR (( 100*(BAUD_ACTUAL - BAUD_RATE) ) / BAUD_RATE)
#if BAUD_ERROR >= 5
#error BAUD_RATE off by greater than 5%
#elif BAUD_ERROR >= 2 && !defined(PRODUCTION)
#warning BAUD_RATE off by greater than 2%
#endif
#endif
#if BAUD_SETTING > 250
#error Unachievable baud rate (too slow) BAUD_RATE
#endif // baud rate slow check
#if (BAUD_SETTING - 1) < 3
#if BAUD_ERROR != 0 // permit high bitrates (ie 1Mbps@16MHz) if error is zero
#error Unachievable baud rate (too fast) BAUD_RATE
#endif
#endif // baud rate fast check
#endif // SOFT_UART
/* Watchdog settings */
#define WATCHDOG_OFF (0)
#define WATCHDOG_16MS (_BV(WDE))
#define WATCHDOG_32MS (_BV(WDP0) | _BV(WDE))
#define WATCHDOG_64MS (_BV(WDP1) | _BV(WDE))
#define WATCHDOG_125MS (_BV(WDP1) | _BV(WDP0) | _BV(WDE))
#define WATCHDOG_250MS (_BV(WDP2) | _BV(WDE))
#define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE))
#define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE))
#define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE))
#ifndef __AVR_ATmega8__
#define WATCHDOG_4S (_BV(WDP3) | _BV(WDE))
#define WATCHDOG_8S (_BV(WDP3) | _BV(WDP0) | _BV(WDE))
#endif
/*
* We can never load flash with more than 1 page at a time, so we can save
* some code space on parts with smaller pagesize by using a smaller int.
*/
#if SPM_PAGESIZE > 255
typedef uint16_t pagelen_t ;
#define GETLENGTH(len) len = getch()<<8; len |= getch()
#else
typedef uint8_t pagelen_t;
#define GETLENGTH(len) (void) getch() /* skip high byte */; len = getch()
#endif
/* Function Prototypes
* The main() function is in init9, which removes the interrupt vector table
* we don't need. It is also 'OS_main', which means the compiler does not
* generate any entry or exit code itself (but unlike 'naked', it doesn't
* supress some compile-time options we want.)
*/
void pre_main(void) __attribute__ ((naked)) __attribute__ ((section (".init8")));
int main(void) __attribute__ ((OS_main)) __attribute__ ((section (".init9"))) __attribute__((used));
void __attribute__((noinline)) __attribute__((leaf)) putch(char);
uint8_t __attribute__((noinline)) __attribute__((leaf)) getch(void) ;
void __attribute__((noinline)) verifySpace();
void __attribute__((noinline)) watchdogConfig(uint8_t x);
#ifdef DEBUG_UART
static void getNch(uint8_t);
#endif
#if LED_START_FLASHES > 0
static inline void flash_led(uint8_t);
#endif
static inline void watchdogReset();
static inline void writebuffer(int8_t memtype, addr16_t mybuff,
addr16_t address, pagelen_t len);
static inline int verify_mem(uint8_t memtype,
addr16_t, pagelen_t len, uint8_t *out_buf);
#ifdef SOFT_UART
void uartDelay() __attribute__ ((naked));
#endif
#include "secret.h"
#include "softspi.h"
#include "mfrc522.h"
/*
* RAMSTART should be self-explanatory. It's bigger on parts with a
* lot of peripheral registers. Let 0x100 be the default
* Note that RAMSTART (for optiboot) need not be exactly at the start of RAM.
*/
#if !defined(RAMSTART) // newer versions of gcc avr-libc define RAMSTART
#define RAMSTART 0x100
#if defined (__AVR_ATmega644P__)
// correct for a bug in avr-libc
#undef SIGNATURE_2
#define SIGNATURE_2 0x0A
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#undef RAMSTART
#define RAMSTART (0x200)
#endif
#endif
/* C zero initialises all global variables. However, that requires */
/* These definitions are NOT zero initialised, but that doesn't matter */
/* This allows us to drop the zero init code, saving us memory */
// static addr16_t buff = {(uint8_t *)(RAMSTART)};
/* Virtual boot partition support */
#ifdef VIRTUAL_BOOT_PARTITION
#define rstVect0_sav (*(uint8_t*)(RAMSTART+SPM_PAGESIZE*2+4))
#define rstVect1_sav (*(uint8_t*)(RAMSTART+SPM_PAGESIZE*2+5))
#define saveVect0_sav (*(uint8_t*)(RAMSTART+SPM_PAGESIZE*2+6))
#define saveVect1_sav (*(uint8_t*)(RAMSTART+SPM_PAGESIZE*2+7))
// Vector to save original reset jump:
// SPM Ready is least probably used, so it's default
// if not, use old way WDT_vect_num,
// or simply set custom save_vect_num in Makefile using vector name
// or even raw number.
#if !defined (save_vect_num)
#if defined (SPM_RDY_vect_num)
#define save_vect_num (SPM_RDY_vect_num)
#elif defined (SPM_READY_vect_num)
#define save_vect_num (SPM_READY_vect_num)
#elif defined (EE_RDY_vect_num)
#define save_vect_num (EE_RDY_vect_num)
#elif defined (EE_READY_vect_num)
#define save_vect_num (EE_READY_vect_num)
#elif defined (WDT_vect_num)
#define save_vect_num (WDT_vect_num)
#else
#error Cant find SPM or WDT interrupt vector for this CPU
#endif
#endif //save_vect_num
// check if it's on the same page (code assumes that)
#if FLASHEND > 8192
// AVRs with more than 8k of flash have 4-byte vectors, and use jmp.
// We save only 16 bits of address, so devices with more than 128KB
// may behave wrong for upper part of address space.
#define rstVect0 2
#define rstVect1 3
#define saveVect0 (save_vect_num*4+2)
#define saveVect1 (save_vect_num*4+3)
#define appstart_vec (save_vect_num*2)
#else
// AVRs with up to 8k of flash have 2-byte vectors, and use rjmp.
#define rstVect0 0
#define rstVect1 1
#define saveVect0 (save_vect_num*2)
#define saveVect1 (save_vect_num*2+1)
#define appstart_vec (save_vect_num)
#endif
#else
#define appstart_vec (0)
#endif // VIRTUAL_BOOT_PARTITION
/* everything that needs to run VERY early */
void pre_main(void) {
// Allow convenient way of calling do_spm function - jump table,
// so entry to this function will always be here, indepedent of compilation,
// features etc
asm volatile (
" rjmp 1f\n"
#ifndef APP_NOSPM
" rjmp do_spm\n"
#else
" ret\n" // if do_spm isn't include, return without doing anything
#endif
"1:\n"
);
}
#ifdef VIRTUAL_BOOT_PARTITION
#error VIRTUAL_BOOT_PARTITION IS NOT IMPLEMENTED :(
#endif
#ifdef DEBUG_UART
#define UDEBUG(x) uart_puts(x);
void uart_puts(char *str) {
while (*str) {
putch(*str++);
}
};
#else
#define UDEBUG(x) ;
#endif
/* main program starts here */
int main(void) {
uint8_t ch;
/*
* Making these local and in registers prevents the need for initializing
* them, and also saves space because code no longer stores to memory.
* (initializing address keeps the compiler happy, but isn't really
* necessary, and uses 4 bytes of flash.)
*/
register addr16_t address;
// After the zero init loop, this is the first code to run.
//
// This code makes the following assumptions:
// No interrupts will execute
// SP points to RAMEND
// r1 contains zero
//
// If not, uncomment the following instructions:
// cli();
asm volatile ("clr __zero_reg__");
#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega8515__) || \
defined(__AVR_ATmega8535__) || defined (__AVR_ATmega16__) || \
defined (__AVR_ATmega32__) || defined (__AVR_ATmega64__) || \
defined (__AVR_ATmega128__) || defined (__AVR_ATmega162__)
SP=RAMEND; // This is done by hardware reset
#endif
/*
* Protect as much from MCUSR as possible for application
* and still skip bootloader if not necessary
*
* Code by MarkG55
* see discusion in https://github.com/Optiboot/optiboot/issues/97
*/
#if defined(__AVR_ATmega8515__) || defined(__AVR_ATmega8535__) || \
defined(__AVR_ATmega16__) || defined(__AVR_ATmega162__) || \
defined (__AVR_ATmega128__)
ch = MCUCSR;
#else
ch = MCUSR;
#endif
// Skip all logic and run bootloader if MCUSR is cleared (application request)
if (ch != 0) {
/*
* To run the boot loader, External Reset Flag must be set.
* If not, we could make shortcut and jump directly to application code.
* Also WDRF set with EXTRF is a result of Optiboot timeout, so we
* shouldn't run bootloader in loop :-) That's why:
* 1. application is running if WDRF is cleared
* 2. we clear WDRF if it's set with EXTRF to avoid loops
* One problematic scenario: broken application code sets watchdog timer
* without clearing MCUSR before and triggers it quickly. But it's
* recoverable by power-on with pushed reset button.
*/
if ((ch & (_BV(WDRF) | _BV(EXTRF))) != _BV(EXTRF)) {
if (ch & _BV(EXTRF)) {
/*
* Clear WDRF because it was most probably set by wdr in bootloader.
* It's also needed to avoid loop by broken application which could
* prevent entering bootloader.
* '&' operation is skipped to spare few bytes as bits in MCUSR
* can only be cleared.
*/
#if defined(__AVR_ATmega8515__) || defined(__AVR_ATmega8535__) || \
defined(__AVR_ATmega16__) || defined(__AVR_ATmega162__) || \
defined(__AVR_ATmega128__)
// Fix missing definitions in avr-libc
MCUCSR = ~(_BV(WDRF));
#else
MCUSR = ~(_BV(WDRF));
#endif
}
/*
* save the reset flags in the designated register
* This can be saved in a main program by putting code in .init0 (which
* executes before normal c init code) to save R2 to a global variable.
*/
__asm__ __volatile__ ("mov r2, %0\n" :: "r" (ch));
// switch off watchdog
watchdogConfig(WATCHDOG_OFF);
// Note that appstart_vec is defined so that this works with either
// real or virtual boot partitions.
__asm__ __volatile__ (
// Jump to 'save' or RST vector
#ifdef VIRTUAL_BOOT_PARTITION
// full code version for virtual boot partition
"ldi r30,%[rstvec]\n"
"clr r31\n"
"ijmp\n"::[rstvec] "M"(appstart_vec)
#else
#ifdef RAMPZ
// use absolute jump for devices with lot of flash
"jmp 0\n"::
#else
// use rjmp to go around end of flash to address 0
// it uses fact that optiboot_version constant is 2 bytes before end of flash
"rjmp optiboot_version+2\n"
#endif //RAMPZ
#endif //VIRTUAL_BOOT_PARTITION
);
}
}
#if LED_START_FLASHES > 0
// Set up Timer 1 for timeout counter
#if defined(__AVR_ATtiny261__)||defined(__AVR_ATtiny461__)||defined(__AVR_ATtiny861__)
TCCR1B = 0x0E; //div 8196 - we could divide by less since it's a 10-bit counter, but why?
#elif defined(__AVR_ATtiny25__)||defined(__AVR_ATtiny45__)||defined(__AVR_ATtiny85__)
TCCR1 = 0x0E; //div 8196 - it's an 8-bit timer.
#elif defined(__AVR_ATtiny43__)
#error "LED flash for Tiny43 not yet supported"
#else
TCCR1B = _BV(CS12) | _BV(CS10); // div 1024
#endif
#endif
#ifdef DEBUG_UART
#ifndef SOFT_UART
#if defined(__AVR_ATmega8__) || defined (__AVR_ATmega8515__) || \
defined (__AVR_ATmega8535__) || defined (__AVR_ATmega16__) || \
defined (__AVR_ATmega32__)
#ifndef SINGLESPEED
UCSRA = _BV(U2X); //Double speed mode USART
#endif //singlespeed
UCSRB = _BV(RXEN) | _BV(TXEN); // enable Rx & Tx
UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0); // config USART; 8N1
UBRRL = (uint8_t)BAUD_SETTING;
#else // mega8/etc
#ifdef LIN_UART
//DDRB|=3;
LINCR = (1 << LSWRES);
//LINBRRL = (((F_CPU * 10L / 32L / BAUD_RATE) + 5L) / 10L) - 1;
LINBRRL=(uint8_t)BAUD_SETTING;
LINBTR = (1 << LDISR) | (8 << LBT0);
LINCR = _BV(LENA) | _BV(LCMD2) | _BV(LCMD1) | _BV(LCMD0);
LINDAT=0;
#else
#ifndef SINGLESPEED
UART_SRA = _BV(U2X0); //Double speed mode USART0
#endif
UART_SRB = _BV(RXEN0) | _BV(TXEN0);
UART_SRC = _BV(UCSZ00) | _BV(UCSZ01);
UART_SRL = (uint8_t)BAUD_SETTING;
#endif // LIN_UART
#endif // mega8/etc
#endif // soft_uart
#endif
// Set up watchdog to trigger after 1s
watchdogConfig(WATCHDOG_4S);
#if (LED_START_FLASHES > 0) || defined(LED_DATA_FLASH) || defined(LED_START_ON)
/* Set LED pin as output */
LED_DDR |= _BV(LED);
#endif
#ifdef DEBUG_UART
#ifdef SOFT_UART
/* Set TX pin as output */
UART_DDR |= _BV(UART_TX_BIT);
#endif
#endif
#if LED_START_FLASHES > 0
/* Flash onboard LED to signal entering of bootloader */
flash_led(LED_START_FLASHES * 2);
#else
#if defined(LED_START_ON)
/* Turn on LED to indicate starting bootloader (less code!) */
LED_PORT |= _BV(LED);
#endif
#endif
PIN_AS_OUTPUT(PIN_NSS_CV520);
PIN_AS_OUTPUT(PIN_NRSTPD);
PIN_AS_OUTPUT(PIN_SCK_CV520);
PIN_AS_OUTPUT(PIN_MOSI_CV520);
PIN_AS_INPUT(PIN_MISO_CV520);
PIN_AS_OUTPUT(PIN_CV520_PWRSW);
PIN_ON(PIN_CV520_PWRSW);
PIN_OFF(PIN_NSS_CV520);
PIN_OFF(PIN_NRSTPD);
PIN_OFF(PIN_SCK_CV520);
uint8_t out_buf[64];
uint8_t prog_buf[128];
for (;;) {
PIN_ON(PIN_NSS_CV520);
PIN_ON(PIN_NRSTPD);
PIN_OFF(PIN_CV520_PWRSW);
_delay_ms(500);
nfc_init();
nfc_transceive(CMD_DESEL, sizeof(CMD_DESEL), 1, 0, out_buf, 32);
int read_bytes = nfc_transceive(CMD_WAKEUP, sizeof(CMD_WAKEUP), 0, 0, out_buf, 32);
if (read_bytes <= 0) {
continue;
}
for (int level = 0; level <= 2; level++) {
out_buf[0] = 0x93 + level * 2;
out_buf[1] = 0x20;
read_bytes = nfc_transceive(out_buf, 2, 0, 0, out_buf, 32);
if (read_bytes != 5)
{
continue;
}
if ((out_buf[0] & out_buf[1] & out_buf[2] & out_buf[3]) == out_buf[4])
{
continue;
}
uint8_t uid0 = out_buf[0];
uint8_t uid1 = out_buf[1];
uint8_t uid2 = out_buf[2];
uint8_t uid3 = out_buf[3];
uint8_t bcc = out_buf[4];
out_buf[0] = 0x93 + level * 2;
out_buf[1] = 0x70;
out_buf[2] = uid0;
out_buf[3] = uid1;
out_buf[4] = uid2;
out_buf[5] = uid3;
out_buf[6] = bcc;
// select card
read_bytes = nfc_transceive(out_buf, 7, 1, 0, out_buf, 32);
if (read_bytes <= 0)
{
continue;
}
if ((out_buf[0] & 4) == 0) {
break;
}
}
read_bytes = nfc_transceive(CMD_REQSAK, sizeof(CMD_REQSAK), 1, 0, out_buf, 32);
if (read_bytes <= 0) {
continue;
}
current_pcb = 0x02;
if (!nfc_transceive_apdu(CMD_APDU_SEL_STK, sizeof(CMD_APDU_SEL_STK), out_buf, 32)) {
continue;
}
if (out_buf[1] != 0xC0 || out_buf[2] != 0xFF) {
continue;
}
UDEBUG("apdu sel\n");
int prog_buf_offset = 0;
uint8_t unlock_attempts = 0;
uint8_t is_locked = 1;
uint8_t last_status = 0;
uint8_t crclsb;
uint8_t crcmsb;
for (;;) {
UDEBUG("enter loop\n");
const uint8_t *next_packet = CMD_APDU_NEXT_CMD_RES_OK;
if (last_status == 1) {
next_packet = CMD_APDU_NEXT_CMD_RES_ERR;
}
read_bytes = nfc_transceive_apdu(next_packet, sizeof(CMD_APDU_NEXT_CMD_RES_OK), out_buf, 64);
if (read_bytes <= 0) {
UDEBUG("fuckup\n");
break;
}
last_status = 0;
UDEBUG("nfc ok\n");
if (unlock_attempts > 3) {
watchdogConfig(WATCHDOG_16MS);
while(1);
}
if (out_buf[1] == 0x37 && read_bytes == 12) {
watchdogReset();
int good = 0;
int bad = 0;
// more or less constant time implementation
for (int i = 0; i < 8; i++) {
if (unlock_key[i] == out_buf[i+2]) {
++good;
} else {
++bad;
}
}
if (good == 8 && bad == 0) {
is_locked = 0;
} else {
last_status = 1;
++unlock_attempts;
}
} else if (out_buf[1] == 0xAA && read_bytes == 6 && !is_locked) {
watchdogReset();
address.bytes[0] = out_buf[2];
address.bytes[1] = out_buf[3];
#ifdef RAMPZ
// Transfer top bit to LSB in RAMPZ
if (address.bytes[1] & 0x80) {
RAMPZ |= 0x01;
}
else {
RAMPZ &= 0xFE;
}
#endif
address.word *= 2; // Convert from word address to byte address
prog_buf_offset = 0;
UDEBUG("ad sel\n");
continue;
} else if (out_buf[1] == 0xF1 && read_bytes == 37 && !is_locked) {
watchdogReset();
iso14443a_crc(out_buf, read_bytes-2, &crclsb, &crcmsb, 0);
if ((out_buf[read_bytes - 2] != crclsb && out_buf[read_bytes - 1] != crcmsb)
|| (prog_buf_offset != out_buf[2])) {
last_status = 1;
continue;
}
for (int i = 0; i < 32; i++) {
prog_buf[i + prog_buf_offset] = out_buf[i + 3];
}
if (prog_buf_offset >= 96) {
UDEBUG("fire\n");
writebuffer('F', (addr16_t) prog_buf, address, 128);
if (!verify_mem('F', address, 128, prog_buf)) {
last_status = 1;
continue;
}
prog_buf_offset = 0;
} else {
UDEBUG("spooled\n");
prog_buf_offset += 32;
}
UDEBUG("lol\n");
continue;
} else if (out_buf[1] == 0x00) {
UDEBUG("done\n");
watchdogConfig(WATCHDOG_16MS);
while(1);
} else {
UDEBUG("no match\n");
last_status = 1;
continue;
}
}
}
}
#ifdef DEBUG_UART
void putch(char ch) {
#ifndef SOFT_UART
#ifndef LIN_UART
while (!(UART_SRA & _BV(UDRE0))) { /* Spin */ }
#else
while (!(LINSIR & _BV(LTXOK))) { /* Spin */ }
#endif
UART_UDR = ch;
#else
__asm__ __volatile__ (
" com %[ch]\n" // ones complement, carry set
" sec\n"
"1: brcc 2f\n"
" cbi %[uartPort],%[uartBit]\n"
" rjmp 3f\n"
"2: sbi %[uartPort],%[uartBit]\n"
" nop\n"
"3: rcall uartDelay\n"
" rcall uartDelay\n"
" lsr %[ch]\n"
" dec %[bitcnt]\n"
" brne 1b\n"
:
:
[bitcnt] "d" (10),
[ch] "r" (ch),
[uartPort] "I" (_SFR_IO_ADDR(UART_PORT)),
[uartBit] "I" (UART_TX_BIT)
:
"r25"
);
#endif
}
uint8_t getch(void) {
uint8_t ch;
#ifdef LED_DATA_FLASH
#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega8515__) || \
defined(__AVR_ATmega8535__) || defined(__AVR_ATmega16__) || \
defined(__AVR_ATmega162__) || defined(__AVR_ATmega32__) || \
defined(__AVR_ATmega64__) || defined(__AVR_ATmega128__)
LED_PORT ^= _BV(LED);
#else
LED_PIN |= _BV(LED);
#endif
#endif
#ifdef SOFT_UART
watchdogReset();
__asm__ __volatile__ (
"1: sbic %[uartPin],%[uartBit]\n" // Wait for start edge
" rjmp 1b\n"
" rcall uartDelay\n" // Get to middle of start bit
"2: rcall uartDelay\n" // Wait 1 bit period
" rcall uartDelay\n" // Wait 1 bit period
" clc\n"
" sbic %[uartPin],%[uartBit]\n"
" sec\n"
" dec %[bitCnt]\n"
" breq 3f\n"
" ror %[ch]\n"
" rjmp 2b\n"
"3:\n"
:
[ch] "=r" (ch)
:
[bitCnt] "d" (9),
[uartPin] "I" (_SFR_IO_ADDR(UART_PIN)),
[uartBit] "I" (UART_RX_BIT)
:
"r25"