USBaspLoader v0.97 stable release
[pub/USBaspLoader.git] / updater / updater.c
1 #ifndef F_CPU
2 #define F_CPU 1000000UL /* 1 Mhz-Takt; hier richtigen Wert eintragen */
3 #endif
4
5
6 #include "../misc/iofixes.h"
7 #include "../firmware/spminterface.h"
8 #include "usbasploader.h"
9
10 // activate updaters full set of features
11 #ifndef CONFIG_UPDATER_REDUCEWRITES
12 #define CONFIG_UPDATER_REDUCEWRITES
13 #endif
14
15 #ifndef CONFIG_UPDATER_CLEANMEMCLEAR
16 #define CONFIG_UPDATER_CLEANMEMCLEAR
17 #endif
18
19
20 #include <avr/interrupt.h>
21 #include <avr/wdt.h>
22
23 #include <avr/pgmspace.h>
24
25 #include <stdint.h>
26
27 #include <util/delay.h>
28 #include <string.h>
29
30 #define updater_pagefillcode ((1<<SPMEN))
31 #define updater_pageerasecode ((1<<PGERS) | (1<<SPMEN))
32 #define updater_pagewritecode ((1<<PGWRT) | (1<<SPMEN))
33
34
35 #include "../firmware/bootloaderconfig.h"
36 #if !HAVE_SPMINTEREFACE
37 #error "bootloader does not support updating itself! (HAVE_SPMINTEREFACE)"
38 #endif
39
40 // helpful definitions and makros ////
41
42 #ifndef NEW_BOOTLOADER_ADDRESS
43 #error "where should the new bootloader be positioned?"
44 #endif
45
46
47
48 #if (NEW_BOOTLOADER_ADDRESS != (funcaddr___bootloader__do_spm-(funcaddr___bootloader__do_spm % SPM_PAGESIZE)))
49 #warning the new bootloader seems to be located elsewhere, than the current one!
50 #endif
51
52 #ifndef NEW_SPM_ADDRESS
53 #warning I do not know where new "bootloader__do_spm" is located - assuming "NEW_BOOTLOADER_ADDRESS+(funcaddr___bootloader__do_spm % SPM_PAGESIZE)"
54 #define NEW_SPM_ADDRESS (NEW_BOOTLOADER_ADDRESS+(funcaddr___bootloader__do_spm % SPM_PAGESIZE))
55 #endif
56
57 // TEMP_SPM supports up to 4 pages for "bootloader__do_spm"...
58 #define TEMP_SPM_NUMPAGE 4
59 #define TEMP_SPM_BLKSIZE (TEMP_SPM_NUMPAGE*SPM_PAGESIZE)
60 #ifndef TEMP_SPM_PAGEADR
61 #warning "TEMP_SPM_PAGEADR" is not defined explicitly - will choose END OF FLASH !
62 #define TEMP_SPM_PAGEADR ((FLASHEND+1) - TEMP_SPM_BLKSIZE)
63 #endif
64 #define TEMP_SPM_ADDRESS ((TEMP_SPM_PAGEADR) + (funcaddr___bootloader__do_spm % SPM_PAGESIZE))
65
66
67 #if (NEW_SPM_ADDRESS == funcaddr___bootloader__do_spm)
68 #define new_do_spm do_spm
69 #else
70 void new_do_spm(const uint32_t flash_byteaddress, const uint8_t spmcrval, const uint16_t dataword) {
71 __do_spm_Ex(flash_byteaddress, spmcrval, dataword, NEW_SPM_ADDRESS >> 1);
72 }
73 #endif
74
75 void temp_do_spm(const uint32_t flash_byteaddress, const uint8_t spmcrval, const uint16_t dataword) {
76 __do_spm_Ex(flash_byteaddress, spmcrval, dataword, TEMP_SPM_ADDRESS >> 1);
77 }
78
79
80
81 // some important consistency checks ////
82
83 //check if "NEW_BOOTLOADER_ADDRESS" is page-aligned
84 #if (NEW_BOOTLOADER_ADDRESS % SPM_PAGESIZE != 0)
85 #error "NEW_BOOTLOADER_ADDRESS" is not aligned to pages!
86 #endif
87
88 //check, if NEW_SPM_ADDRESS is an even address
89 #if ((NEW_SPM_ADDRESS % 2) != 0)
90 #error NEW_SPM_ADDRESS must be an even address, since it contains executable code!
91 #endif
92
93
94
95 //check, if TEMP_SPM somehow overlaps with old SPM
96 #if (((TEMP_SPM_ADDRESS + TEMP_SPM_BLKSIZE + SPM_PAGESIZE) >= funcaddr___bootloader__do_spm) && (TEMP_SPM_ADDRESS <= (funcaddr___bootloader__do_spm + TEMP_SPM_BLKSIZE + SPM_PAGESIZE)))
97 #error TEMP_SPM_ADDRESS overlaps "funcaddr___bootloader__do_spm"!
98 #endif
99
100 //check, if TEMP_SPM somehow overlaps with new SPM
101 #if (((TEMP_SPM_ADDRESS + TEMP_SPM_BLKSIZE + SPM_PAGESIZE) >= NEW_SPM_ADDRESS) && (TEMP_SPM_ADDRESS <= (NEW_SPM_ADDRESS + TEMP_SPM_BLKSIZE + SPM_PAGESIZE)))
102 #error TEMP_SPM_ADDRESS overlaps "NEW_SPM_ADDRESS"!
103 #endif
104
105 //check, if TEMP_SPM_ADDRESS is an even address
106 #if ((TEMP_SPM_ADDRESS % 2) != 0)
107 #error TEMP_SPM_ADDRESS must be an even address, since it contains executable code!
108 #endif
109
110 //check, if TEMP_SPM_ADDRESS fits into flash
111 #if ((TEMP_SPM_PAGEADR + TEMP_SPM_BLKSIZE) > (FLASHEND+1))
112 #error TEMP_SPM_ADDRESS exceeds flashend!
113 #endif
114
115 //check if size too low
116 #if (SIZEOF_new_firmware <= (TEMP_SPM_BLKSIZE + (NEW_SPM_ADDRESS - NEW_BOOTLOADER_ADDRESS)))
117 #error empty firmware!
118 #endif
119
120 //check if size too high
121 #if (SIZEOF_new_firmware > ((FLASHEND+1)-NEW_BOOTLOADER_ADDRESS))
122 #error firmware too big! firmware does not fit into flash memory!
123 #endif
124
125 // main code ////
126
127 /*
128 * in this case a near address
129 */
130 typedef uint32_t mypgm_addr_t;
131 typedef void (*mypgm_spminterface)(const uint32_t flash_byteaddress, const uint8_t spmcrval, const uint16_t dataword);
132
133 #if FLASHEND > 65535
134 # define FULLCORRECTFLASHADDRESS(addr) (((mypgm_addr_t)(addr)) | (((mypgm_addr_t)FLASHADDRESS) & ((mypgm_addr_t)0xffff0000)))
135 # define mymemcpy_PF mymemcpy_PF_far
136 void *mymemcpy_PF_far (void *dest, mypgm_addr_t src, size_t n) {
137 uint8_t *pagedata = (void*)dest;
138 mypgm_addr_t pageaddr = src;
139 size_t i;
140
141 for (i=0;i<n;i+=1) {
142 pagedata[i]=pgm_read_byte_far(pageaddr);
143 pageaddr+=1;
144 }
145
146 return dest;
147 }
148 #else
149 # define FULLCORRECTFLASHADDRESS(addr) (addr)
150 # define mymemcpy_PF memcpy_PF
151 #endif
152
153 #if 0
154 size_t mypgm_readpage(const mypgm_addr_t byteaddress,const void* buffer, const size_t bufferbytesize) {
155 size_t result = (bufferbytesize < SPM_PAGESIZE)?bufferbytesize:SPM_PAGESIZE;
156 size_t pagesize = result >> 1;
157 uint16_t *pagedata = (void*)buffer;
158 mypgm_addr_t pageaddr = byteaddress - (byteaddress % SPM_PAGESIZE);
159 size_t i;
160
161 for (i=0;i<pagesize;i+=1) {
162 pagedata[i]=pgm_read_word_far(pageaddr);
163 pageaddr+=2;
164 }
165
166 return result;
167 }
168 #else
169 // replace it somehow with "memcpy_PF" in order to save some ops...
170 size_t mypgm_readpage(const mypgm_addr_t byteaddress,const void* buffer, const size_t bufferbytesize) {
171 size_t result = (bufferbytesize < SPM_PAGESIZE)?bufferbytesize:SPM_PAGESIZE;
172 mypgm_addr_t pageaddr = byteaddress - (byteaddress % SPM_PAGESIZE);
173
174 mymemcpy_PF((void*)buffer, (uint_farptr_t)pageaddr, result);
175
176 return result;
177 }
178 #endif
179
180 #ifdef CONFIG_UPDATER_REDUCEWRITES
181 size_t mypgm_WRITEpage(const mypgm_addr_t byteaddress,const void* buffer, const size_t bufferbytesize, mypgm_spminterface spmfunc) {
182 size_t result = (bufferbytesize < SPM_PAGESIZE)?bufferbytesize:SPM_PAGESIZE;
183 size_t pagesize = result >> 1;
184 uint16_t *pagedata = (void*)buffer;
185 mypgm_addr_t pageaddr_bakup = byteaddress - (byteaddress % SPM_PAGESIZE);
186 mypgm_addr_t pageaddr = pageaddr_bakup;
187
188 uint8_t changed=0, needs_erase=0;
189 uint16_t deeword;
190 size_t i;
191
192 // just check, if page needs a rewrite or an erase...
193 for (i=0;i<pagesize;i+=1) {
194 #if (FLASHEND > 65535)
195 deeword=pgm_read_word_far(pageaddr);
196 #else
197 deeword=pgm_read_word(pageaddr);
198 #endif
199
200 if (deeword != pagedata[i]) changed=1;
201
202 /*
203 * deeword = x
204 * buffer = y
205 *
206 * 1 ? 1 ==> 1
207 * 1 ? 0 ==> 1
208 * 0 ? 1 ==> 0
209 * 0 ? 0 ==> 1
210 *
211 * ==> /(/x * y) ==> x + /y
212 */
213 deeword |= ~pagedata[i];
214 if ((~deeword) != 0) needs_erase=1;
215
216 pageaddr+=2;
217 }
218
219 if (changed) {
220
221 if (needs_erase) {
222 //do a page-erase, ATTANTION: flash only can be erased a limited number of times !
223 spmfunc(pageaddr_bakup, updater_pageerasecode, 0);
224 }
225
226 // from now on, the page is assumed empty !! (hopefully our code is located somewhere else!)
227 // now, fill the tempoary buffer
228 // ATTANTION: see comment on "do_spm" !
229 pageaddr = pageaddr_bakup;
230 for (i=0;i<pagesize;i+=1) {
231 spmfunc(pageaddr, updater_pagefillcode, pagedata[i]);
232 pageaddr+=2;
233 }
234
235 // so, now finally write the page to the FLASH
236 spmfunc(pageaddr_bakup, updater_pagewritecode, 0);
237 } else {
238 // no change - no write...
239 result = 0;
240 }
241
242
243 return result;
244 }
245 #else
246 size_t mypgm_WRITEpage(const mypgm_addr_t byteaddress,const void* buffer, const size_t bufferbytesize, mypgm_spminterface spmfunc) {
247 size_t result = (bufferbytesize < SPM_PAGESIZE)?bufferbytesize:SPM_PAGESIZE;
248 size_t pagesize = result >> 1;
249 uint16_t *pagedata = (void*)buffer;
250 mypgm_addr_t pageaddr_bakup = byteaddress - (byteaddress % SPM_PAGESIZE);
251 mypgm_addr_t pageaddr = pageaddr_bakup;
252
253 size_t i;
254
255 //do a page-erase, ATTANTION: flash only can be erased a limited number of times !
256 spmfunc(pageaddr_bakup, updater_pageerasecode, 0);
257
258 // from now on, the page is assumed empty !! (hopefully our code is located somewhere else!)
259 // now, fill the tempoary buffer
260 // ATTANTION: see comment on "do_spm" !
261 pageaddr = pageaddr_bakup;
262 for (i=0;i<pagesize;i+=1) {
263 spmfunc(pageaddr, updater_pagefillcode, pagedata[i]);
264 pageaddr+=2;
265 }
266
267 // so, now finally write the page to the FLASH
268 spmfunc(pageaddr_bakup, updater_pagewritecode, 0);
269
270 return result;
271 }
272 #endif
273
274 #if defined(UPDATECRC32)
275 #include "crccheck.c"
276 #endif
277
278 // #pragma GCC diagnostic ignored "-Wno-pointer-to-int-cast"
279 int main(void)
280 {
281 #if defined(UPDATECRC32)
282 uint32_t crcval;
283 #endif
284 size_t i;
285 uint8_t buffer[SPM_PAGESIZE];
286
287 wdt_disable();
288 cli();
289
290 #if defined(UPDATECRC32)
291 // check if new firmware-image is corrupted
292 crcval = D_32;
293 for (i=0;i<SIZEOF_new_firmware;i+=1) {
294 #if (FLASHEND > 65535)
295 crcval = update_crc_32(crcval, pgm_read_byte_far(FULLCORRECTFLASHADDRESS(&new_firmware[i])));
296 #else
297 crcval = update_crc_32(crcval, pgm_read_byte(FULLCORRECTFLASHADDRESS(&new_firmware[i])));
298 #endif
299 }
300 crcval ^= D_32;
301
302 // allow to change the firmware
303 if (crcval == ((uint32_t)UPDATECRC32)) {
304 #endif
305
306 // check if firmware would change...
307 buffer[0]=0;
308 for (i=0;i<SIZEOF_new_firmware;i+=2) {
309 uint16_t a, b;
310 #if (FLASHEND > 65535)
311 a=pgm_read_word_far(FULLCORRECTFLASHADDRESS(&new_firmware[i]));
312 b=pgm_read_word_far(NEW_BOOTLOADER_ADDRESS+i);
313 #else
314 a=pgm_read_word(FULLCORRECTFLASHADDRESS(&new_firmware[i]));
315 b=pgm_read_word(NEW_BOOTLOADER_ADDRESS+i);
316 #endif
317 if (a!=b) {
318 buffer[0]=1;
319 break;
320 }
321 }
322
323
324
325 // need to change the firmware...
326 if (buffer[0]) {
327
328 // A
329 // copy the current "bootloader__do_spm" to tempoary position via std. "bootloader__do_spm"
330 for (i=0;i<TEMP_SPM_BLKSIZE;i+=SPM_PAGESIZE) {
331 mypgm_WRITEpage(TEMP_SPM_PAGEADR+i, buffer, mypgm_readpage(funcaddr___bootloader__do_spm+i, buffer, sizeof(buffer)), do_spm);
332 }
333
334 // B
335 // start updating the firmware to "NEW_BOOTLOADER_ADDRESS" until at least "TEMP_SPM_BLKSIZE"-bytes after "NEW_SPM_ADDRESS" were written
336 // therefore use the tempoary "bootloader__do_spm" (since we most probably will overwrite the default do_spm)
337 for (i=0;;i+=SPM_PAGESIZE) {
338 #ifdef CONFIG_UPDATER_CLEANMEMCLEAR
339 memset((void*)buffer, 0xff, sizeof(buffer));
340 #endif
341 mymemcpy_PF((void*)buffer, (uint_farptr_t)(FULLCORRECTFLASHADDRESS(&new_firmware[i])), ((SIZEOF_new_firmware-i)>sizeof(buffer))?sizeof(buffer):(SIZEOF_new_firmware-i));
342
343 mypgm_WRITEpage(NEW_BOOTLOADER_ADDRESS+i, buffer, sizeof(buffer), temp_do_spm);
344
345 if ((NEW_BOOTLOADER_ADDRESS+i) > (NEW_SPM_ADDRESS+TEMP_SPM_BLKSIZE)) break;
346 }
347
348 // C
349 // continue writeing the new_firmware after "NEW_SPM_ADDRESS+TEMP_SPM_BLKSIZE" this time use the "new_do_spm"
350 for (;i<SIZEOF_new_firmware;i+=SPM_PAGESIZE) {
351 #ifdef CONFIG_UPDATER_CLEANMEMCLEAR
352 memset((void*)buffer, 0xff, sizeof(buffer));
353 #endif
354 mymemcpy_PF((void*)buffer, (uint_farptr_t)(FULLCORRECTFLASHADDRESS(&new_firmware[i])), ((SIZEOF_new_firmware-i)>sizeof(buffer))?sizeof(buffer):(SIZEOF_new_firmware-i));
355
356 mypgm_WRITEpage(NEW_BOOTLOADER_ADDRESS+i, buffer, sizeof(buffer), new_do_spm);
357
358 }
359
360
361
362 }
363
364 #if defined(UPDATECRC32)
365 }
366 #endif
367
368 return 0;
369 }