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