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