10
10
# Authors:
11
11
# - 2022, Andrey Kunitsyn
12
12
13
+ """
14
+ ### elf2uf2 - Elf to UF2 Converter
15
+
16
+ UF2 file format info : https://github.com/microsoft/uf2
17
+
18
+ ```sh
19
+ python3 modm/modm_tools/elf2uf2.py firmware.elf -o firmware.uf2 --target rp2040 \
20
+ --range 0x10000000:0x15000000:CONTENTS \
21
+ --range 0x20000000:0x20042000:NO_CONTENTS
22
+
23
+ # converts `firmware.elf` to `firmware.uf2` in the CWD
24
+ ```
25
+ """
26
+
13
27
import os
14
28
import struct
15
29
16
30
from pathlib import Path
17
31
18
32
verbose = False
19
33
20
- # we require 256 (as this is the page size supported by the device)
21
- LOG2_PAGE_SIZE = 8 # @todo configurable
22
- PAGE_SIZE = (1 << LOG2_PAGE_SIZE )
23
34
24
- # @todo read from modm-device
25
- memory_map = {
26
- "rp2040" : {
27
- "flash" : [
28
- {"start" :0x10000000 ,"end" :0x15000000 ,"type" :"CONTENTS" },
29
- {"start" :0x20000000 ,"end" :0x20042000 ,"type" :"NO_CONTENTS" },
30
- {"start" :0x21000000 ,"end" :0x21040000 ,"type" :"NO_CONTENTS" },
31
- ],
32
- "ram" : [
33
- {"start" :0x20000000 ,"end" :0x20042000 ,"type" :"CONTENTS" },
34
- {"start" :0x15000000 ,"end" :0x15004000 ,"type" :"CONTENTS" },
35
- {"start" :0x00000000 ,"end" :0x00004000 ,"type" :"IGNORE" }, #// for now we ignore the bootrom if present
36
- ]
37
- }
38
- }
39
35
40
36
UF2_FLAG_NOT_MAIN_FLASH = 0x00000001
41
37
UF2_FLAG_FILE_CONTAINER = 0x00001000
49
45
"MAGIC_START1" : 0x9E5D5157 ,
50
46
"MAGIC_END" : 0x0AB16F30 ,
51
47
"FAMILY_ID" : 0xe48bff56 ,
48
+ "PAGE_SIZE" : (1 << 8 ),
52
49
}
53
50
}
54
51
@@ -151,7 +148,7 @@ def check_address_range(valid_ranges, addr, vaddr, size, uninitialized):
151
148
return range
152
149
raise Exception ("Memory segment {:08x}->{:08x} is outside of valid address range for device" .format (addr , addr + size ))
153
150
154
- def read_and_check_elf32_ph_entries (buffer , eh , valid_ranges , pages ):
151
+ def read_and_check_elf32_ph_entries (buffer , eh , valid_ranges , pages , page_size ):
155
152
for i in range (eh [6 ]):
156
153
entry = struct .unpack_from (elf32_ph_entry ,buffer ,eh [1 ]+ i * elf32_ph_entry_size )
157
154
if entry [0 ] == PT_LOAD and entry [5 ] != 0 :
@@ -167,8 +164,8 @@ def read_and_check_elf32_ph_entries(buffer, eh, valid_ranges, pages):
167
164
remaining = mapped_size ;
168
165
file_offset = entry [1 ];
169
166
while remaining > 0 :
170
- off = addr & (PAGE_SIZE - 1 )
171
- chlen = min (remaining , PAGE_SIZE - off )
167
+ off = addr & (page_size - 1 )
168
+ chlen = min (remaining , page_size - off )
172
169
key = addr - off
173
170
fragments = []
174
171
if key in pages :
@@ -201,32 +198,17 @@ def realize_page(buffer, fragments):
201
198
raise Exception ("failed concat" )
202
199
return result
203
200
204
- def is_address_initialized (valid_ranges , addr ):
205
- for range in valid_ranges :
206
- if range ["start" ] <= addr and range ["end" ] > addr :
207
- return range ["type" ] == "CONTENTS"
208
- return False
209
201
210
- def convert_data (source_bytes ,target ):
202
+ def convert_data (source_bytes ,target , ranges ):
211
203
eh = read_header (source_bytes )
212
- ranges = None
213
- ranges_name = None
214
- memory_variants = memory_map [target ]
215
- for name ,memranges in memory_variants .items ():
216
- if is_address_initialized (memranges ,eh [0 ]):
217
- ranges = memranges
218
- ranges_name = name
219
- break
220
- if not ranges :
221
- raise Exception ("Not found memory range" )
204
+ config = uf2_config [target ]
222
205
if verbose :
223
- print ('Build for chip:{} mem:{} ' .format (target , ranges_name ))
206
+ print ('Build for chip:{}' .format (target ))
224
207
pages = {}
225
- read_and_check_elf32_ph_entries (source_bytes ,eh ,ranges ,pages )
208
+ read_and_check_elf32_ph_entries (source_bytes ,eh ,ranges ,pages , config [ "PAGE_SIZE" ] )
226
209
if len (pages ) == 0 :
227
210
raise Exception ("The input file has no memory pages" )
228
211
229
- config = uf2_config [target ]
230
212
num_blocks = len (pages )
231
213
page_num = 0
232
214
file_content = bytes (0 )
@@ -240,7 +222,7 @@ def convert_data(source_bytes,target):
240
222
config ["MAGIC_START1" ],
241
223
UF2_FLAG_FAMILY_ID_PRESENT ,
242
224
target_addr ,
243
- PAGE_SIZE ,
225
+ config [ " PAGE_SIZE" ] ,
244
226
page_num ,
245
227
num_blocks ,
246
228
config ["FAMILY_ID" ]) + data + struct .pack ('<I' ,config ["MAGIC_END" ])
@@ -250,11 +232,16 @@ def convert_data(source_bytes,target):
250
232
file_content += block
251
233
return file_content
252
234
253
- def convert (source , output , target ):
235
+ def convert (source , output , target , ranges ):
254
236
source_bytes = Path (source ).read_bytes ()
255
- uf2 = convert_data (source_bytes ,target )
237
+ uf2 = convert_data (source_bytes ,target , ranges )
256
238
Path (output ).write_bytes (uf2 )
257
239
240
+ def parse_range (strval ):
241
+ if strval .startswith ('0x' ):
242
+ return int (strval [2 :],16 )
243
+ return int (strval )
244
+
258
245
if __name__ == "__main__" :
259
246
import argparse
260
247
@@ -266,6 +253,7 @@ def convert(source, output, target):
266
253
parser .add_argument (
267
254
"-o" , "--output" ,
268
255
dest = "output" ,
256
+ required = True ,
269
257
help = "Destination UF2 image" )
270
258
parser .add_argument (
271
259
"--verbose" ,
@@ -277,8 +265,21 @@ def convert(source, output, target):
277
265
dest = "target" ,
278
266
default = "rp2040" ,
279
267
help = "Target chip" )
268
+ parser .add_argument (
269
+ "--range" ,
270
+ nargs = '+' ,
271
+ dest = "ranges" ,
272
+ help = "Memory range in format start:end:type, where type NO_CONTENTS|CONTENTS|IGNORE" )
280
273
281
274
args = parser .parse_args ()
282
275
verbose = args .verbose
276
+ ranges = []
277
+ for r in args .ranges :
278
+ start ,end ,t = r .split (':' )
279
+ ranges .append ({
280
+ "start" : parse_range (start ),
281
+ "end" : parse_range (end ),
282
+ "type" : t
283
+ })
283
284
284
285
convert (args .source ,args .output ,args .target )
0 commit comments