@@ -16,9 +16,6 @@ import time
1616
1717IMAGE_SIZE = 512 * 1024
1818
19- # Larger files won't with with "vcgencmd bootloader_config"
20- MAX_FILE_SIZE = 2024
21- ALIGN_SIZE = 4096
2219BOOTCONF_TXT = 'bootconf.txt'
2320BOOTCONF_SIG = 'bootconf.sig'
2421PUBKEY_BIN = 'pubkey.bin'
@@ -39,6 +36,11 @@ FILE_HDR_LEN = 20
3936FILENAME_LEN = 12
4037TEMP_DIR = None
4138
39+ # Modifiable files are stored in a single 4K erasable sector.
40+ # The max content 4076 bytes because of the file header.
41+ ERASE_ALIGN_SIZE = 4096
42+ MAX_FILE_SIZE = ERASE_ALIGN_SIZE - FILE_HDR_LEN
43+
4244DEBUG = False
4345def debug (s ):
4446 if DEBUG :
@@ -221,7 +223,7 @@ class ImageSection:
221223 self .offset = offset
222224 self .length = length
223225 self .filename = filename
224- debug ("ImageSection %x %x %x %s" % (magic , offset , length , filename ))
226+ debug ("ImageSection %x offset %d length %d %s" % (magic , offset , length , filename ))
225227
226228class BootloaderImage (object ):
227229 def __init__ (self , filename , output = None ):
@@ -250,7 +252,6 @@ class BootloaderImage(object):
250252 """
251253 offset = 0
252254 magic = 0
253- found = False
254255 while offset < IMAGE_SIZE :
255256 magic , length = struct .unpack_from ('>LL' , self ._bytes , offset )
256257 if magic == 0x0 or magic == 0xffffffff :
@@ -262,6 +263,7 @@ class BootloaderImage(object):
262263 if magic == FILE_MAGIC : # Found a file
263264 # Discard trailing null characters used to pad filename
264265 filename = self ._bytes [offset + 8 : offset + FILE_HDR_LEN ].decode ('utf-8' ).replace ('\0 ' , '' )
266+ debug ("section at %d length %d magic %08x %s" % (offset , length , magic , filename ))
265267 self ._sections .append (ImageSection (magic , offset , length , filename ))
266268
267269 offset += 8 + length # length + type
@@ -272,26 +274,46 @@ class BootloaderImage(object):
272274 Returns the offset, length and whether this is the last section in the
273275 EEPROM for a modifiable file within the image.
274276 """
275- ret = (- 1 , - 1 , False )
277+ offset = - 1
278+ length = - 1
279+ is_last = False
280+
281+ next_offset = IMAGE_SIZE - ERASE_ALIGN_SIZE # Don't create padding inside the bootloader scratch page
276282 for i in range (0 , len (self ._sections )):
277283 s = self ._sections [i ]
278284 if s .magic == FILE_MAGIC and s .filename == filename :
279285 is_last = (i == len (self ._sections ) - 1 )
280- ret = (s .offset , s .length , is_last )
286+ offset = s .offset
287+ length = s .length
288+ break
289+
290+ # Find the start of the next non padding section
291+ i += 1
292+ while i < len (self ._sections ):
293+ if self ._sections [i ].magic == PAD_MAGIC :
294+ i += 1
295+ else :
296+ next_offset = self ._sections [i ].offset
281297 break
282- debug ('%s offset %d length %d last %s' % (filename , ret [0 ], ret [1 ], ret [2 ]))
298+ ret = (offset , length , is_last , next_offset )
299+ debug ('%s offset %d length %d is-last %d next %d' % (filename , ret [0 ], ret [1 ], ret [2 ], ret [3 ]))
283300 return ret
284301
285302 def update (self , src_bytes , dst_filename ):
286303 """
287304 Replaces a modifiable file with specified byte array.
288305 """
289- hdr_offset , length , is_last = self .find_file (dst_filename )
306+ hdr_offset , length , is_last , next_offset = self .find_file (dst_filename )
307+ update_len = len (src_bytes ) + FILE_HDR_LEN
308+
309+ if hdr_offset + update_len > IMAGE_SIZE - ERASE_ALIGN_SIZE :
310+ raise Exception ('No space available - image past EOF.' )
311+
290312 if hdr_offset < 0 :
291313 raise Exception ('Update target %s not found' % dst_filename )
292314
293- if hdr_offset + len ( src_bytes ) + FILE_HDR_LEN > IMAGE_SIZE :
294- raise Exception ('EEPROM image size exceeded' )
315+ if hdr_offset + update_len > next_offset :
316+ raise Exception ('Update %d bytes is larger than section size %d' % ( update_len , next_offset - hdr_offset ) )
295317
296318 new_len = len (src_bytes ) + FILENAME_LEN + 4
297319 struct .pack_into ('>L' , self ._bytes , hdr_offset + 4 , new_len )
@@ -312,7 +334,7 @@ class BootloaderImage(object):
312334 # by convention bootconf.txt is the last section and there's no need to
313335 # pad to the end of the sector. This also ensures that the loopback
314336 # config read/write tests produce identical binaries.
315- pad_bytes = ALIGN_SIZE - ( pad_start % ALIGN_SIZE )
337+ pad_bytes = next_offset - pad_start
316338 if pad_bytes > 8 and not is_last :
317339 pad_bytes -= 8
318340 struct .pack_into ('>i' , self ._bytes , pad_start , PAD_MAGIC )
@@ -358,10 +380,17 @@ class BootloaderImage(object):
358380 sys .stdout .write (self ._bytes )
359381
360382 def get_file (self , filename ):
361- hdr_offset , length , is_last = self .find_file (filename )
383+ hdr_offset , length , is_last , next_offset = self .find_file (filename )
362384 offset = hdr_offset + 4 + FILE_HDR_LEN
363- config_bytes = self ._bytes [offset :offset + length - FILENAME_LEN - 4 ]
364- return config_bytes
385+ file_bytes = self ._bytes [offset :offset + length - FILENAME_LEN - 4 ]
386+ return file_bytes
387+
388+ def extract_files (self ):
389+ for i in range (0 , len (self ._sections )):
390+ s = self ._sections [i ]
391+ if s .magic == FILE_MAGIC :
392+ file_bytes = self .get_file (s .filename )
393+ open (s .filename , 'wb' ).write (file_bytes )
365394
366395 def read (self ):
367396 config_bytes = self .get_file ('bootconf.txt' )
@@ -457,6 +486,7 @@ See 'rpi-eeprom-update -h' for more information about the available EEPROM image
457486 parser .add_argument ('-o' , '--out' , help = 'Name of output file' , required = False )
458487 parser .add_argument ('-d' , '--digest' , help = 'Signed boot only. The name of the .sig file generated by rpi-eeprom-dgst for config.txt ' , required = False )
459488 parser .add_argument ('-p' , '--pubkey' , help = 'Signed boot only. The name of the RSA public key file to store in the EEPROM' , required = False )
489+ parser .add_argument ('-x' , '--extract' , action = 'store_true' , default = False , help = 'Extract the modifiable files (boot.conf, pubkey, signature)' , required = False )
460490 parser .add_argument ('eeprom' , nargs = '?' , help = 'Name of EEPROM file to use as input' )
461491 args = parser .parse_args ()
462492
@@ -468,6 +498,9 @@ See 'rpi-eeprom-update -h' for more information about the available EEPROM image
468498
469499 if args .edit :
470500 edit_config (args .eeprom )
501+ elif args .eeprom is not None and args .extract :
502+ image = BootloaderImage (args .eeprom , args .out )
503+ image .extract_files ()
471504 elif args .apply is not None :
472505 if not os .path .exists (args .apply ):
473506 exit_error ("config file '%s' not found" % args .apply )
0 commit comments