OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 2 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """ | 6 """ |
7 This module provides convenience routines to access Flash ROM (EEPROM). | 7 This module provides convenience routines to access Flash ROM (EEPROM). |
8 - flashrom_util is a low level wrapper of flashrom(8) program. | 8 - flashrom_util is a low level wrapper of flashrom(8) program. |
9 - FlashromUtility is a high level object which provides more advanced | 9 - FlashromUtility is a high level object which provides more advanced |
10 features like journaling-alike (log-based) changing. | 10 features like journaling-alike (log-based) changing. |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
60 FV_GBB = 0x20000, | 60 FV_GBB = 0x20000, |
61 FV_BSTUB = 0x40000, | 61 FV_BSTUB = 0x40000, |
62 """, | 62 """, |
63 "ec": """ | 63 "ec": """ |
64 EC_RO | 64 EC_RO |
65 | | 65 | |
66 EC_RW | 66 EC_RW |
67 """, | 67 """, |
68 } | 68 } |
69 | 69 |
| 70 # The default conversion table for fmap_decode. |
| 71 DEFAULT_CHROMEOS_FMAP_CONVERSION = { |
| 72 "Boot Stub": "FV_BSTUB", |
| 73 "GBB Area": "FV_GBB", |
| 74 "Recovery Firmware": "FVDEV", |
| 75 "RO VPD": "RO_VPD", |
| 76 "Firmware A Key": "VBOOTA", |
| 77 "Firmware A Data": "FVMAIN", |
| 78 "Firmware B Key": "VBOOTB", |
| 79 "Firmware B Data": "FVMAINB", |
| 80 "Log Volume": "FV_LOG", |
| 81 } |
| 82 |
70 # Default "skip" sections when verifying section data. | 83 # Default "skip" sections when verifying section data. |
71 # This is required because some flashrom chip may create timestamps (or checksum | 84 # This is required because some flashrom chip may create timestamps (or checksum |
72 # values) when (or immediately after) we change flashrom content. | 85 # values) when (or immediately after) we change flashrom content. |
73 # The syntax is a comma-separated list of string tuples (separated by ':'): | 86 # The syntax is a comma-separated list of string tuples (separated by ':'): |
74 # PARTNAME:OFFSET:SIZE | 87 # PARTNAME:OFFSET:SIZE |
75 # If there's no need to skip anything, provide an empty list []. | 88 # If there's no need to skip anything, provide an empty list []. |
76 DEFAULT_CHROMEOS_FIRMWARE_SKIP_VERIFY_LIST = { | 89 DEFAULT_CHROMEOS_FIRMWARE_SKIP_VERIFY_LIST = { |
77 "bios": [], | 90 "bios": [], |
78 "ec": "EC_RO:0x48:4", | 91 "ec": "EC_RO:0x48:4", |
79 } | 92 } |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 # fill sections | 186 # fill sections |
174 for i in range(len(names)): | 187 for i in range(len(names)): |
175 # ignore unamed sections | 188 # ignore unamed sections |
176 if names[i] != '*': | 189 if names[i] != '*': |
177 layout[names[i]] = (offset, offset + sizes[i] - 1) | 190 layout[names[i]] = (offset, offset + sizes[i] - 1) |
178 offset = offset + sizes[i] | 191 offset = offset + sizes[i] |
179 | 192 |
180 return layout | 193 return layout |
181 | 194 |
182 | 195 |
| 196 def _convert_fmap_layout(conversion_map, fmap_areas): |
| 197 """ |
| 198 (internal utility) Converts a FMAP areas structure to flashrom layout format |
| 199 by conversion_map. |
| 200 Args: |
| 201 conversion_map: dictionary of names to convert. |
| 202 fmap_areas: a list of {name, offset, size} dictionary. |
| 203 |
| 204 Returns: layout structure for flashrom_util, or empty for failure. |
| 205 """ |
| 206 layout = {} |
| 207 for entry in fmap_areas: |
| 208 name = entry['name'] |
| 209 offset = entry['offset'] |
| 210 size = entry['size'] |
| 211 if name in conversion_map: |
| 212 name = conversion_map[name] |
| 213 name = name.replace(' ', '%20') |
| 214 layout[name] = (offset, offset + size - 1) |
| 215 return layout |
| 216 |
| 217 |
| 218 def decode_fmap_layout_external(conversion_map, image_file_name): |
| 219 """ |
| 220 (Utility) Decodes layout by an external fmap_decode command provided by |
| 221 system PATH. |
| 222 Args: |
| 223 conversion_map: dictionary for FMAP area name conversion |
| 224 image_file_name: file name of firmware image with FMAP structure for |
| 225 parsing layout. |
| 226 |
| 227 Returns: layout structure for flashrom_util, or empty for failure. |
| 228 """ |
| 229 if os.system('type fmap_decode 2>/dev/null') != 0: |
| 230 # print 'error: no fmap_decode in system.' |
| 231 return {} |
| 232 fmap_object = [] |
| 233 fmap = os.popen("fmap_decode %s 2>/dev/null" % image_file_name).readlines() |
| 234 for entry in fmap: |
| 235 if 'area_name' not in entry: |
| 236 continue |
| 237 # format: area_offset="HEX" area_size="HEX" area_name="NAME" ... |
| 238 offset = int(re.findall('area_offset="([^"]*)"', entry)[0], 0) |
| 239 size = int(re.findall('area_size="([^"]*)"', entry)[0], 0) |
| 240 name = re.findall('area_name="([^"]*)"', entry)[0] |
| 241 # print 'off=%s, size=%s, name=%s' % (offset, size, name) |
| 242 fmap_object.append({'offset':offset, 'size':size, 'name':name}) |
| 243 return _convert_fmap_layout(conversion_map, fmap_object) |
| 244 |
| 245 |
| 246 def decode_fmap_layout(conversion_map, image_blob): |
| 247 """ |
| 248 (Utility) Uses fmap_decode to retrieve embedded layout of a prepared |
| 249 firmware image. |
| 250 Args: |
| 251 conversion_map: dictionary for FMAP area name conversion |
| 252 image_blob: binary data of firmware image containing FMAP |
| 253 |
| 254 Returns: layout structure for flashrom_util, or empty for failure. |
| 255 """ |
| 256 try: |
| 257 import site_fmap |
| 258 fmap_object = site_fmap.fmap_decode(image_blob)['areas'] |
| 259 except: |
| 260 # print 'decode_fmap_layout: failed to decode from image blob' |
| 261 fmap_object = [] |
| 262 return _convert_fmap_layout(conversion_map, fmap_object) |
| 263 |
| 264 |
183 def csv_to_list(csv, delimiter=','): | 265 def csv_to_list(csv, delimiter=','): |
184 """ | 266 """ |
185 (Utility) Converts a comma-separated-value (or list) to a list. | 267 (Utility) Converts a comma-separated-value (or list) to a list. |
186 | 268 |
187 To use symbols other that comma, customize with delimiter. | 269 To use symbols other that comma, customize with delimiter. |
188 """ | 270 """ |
189 if isinstance(csv, types.StringTypes): | 271 if isinstance(csv, types.StringTypes): |
190 return [i.strip() for i in csv.split(delimiter)] | 272 return [i.strip() for i in csv.split(delimiter)] |
191 return csv | 273 return csv |
192 | 274 |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
258 verbose=False, | 340 verbose=False, |
259 keep_temp_files=False, | 341 keep_temp_files=False, |
260 target_map=None): | 342 target_map=None): |
261 """ constructor of flashrom_util. help(flashrom_util) for more info """ | 343 """ constructor of flashrom_util. help(flashrom_util) for more info """ |
262 self.tool_path = tool_path | 344 self.tool_path = tool_path |
263 self.cmd_prefix = cmd_prefix | 345 self.cmd_prefix = cmd_prefix |
264 self.tmp_root = tmp_root | 346 self.tmp_root = tmp_root |
265 self.verbose = verbose | 347 self.verbose = verbose |
266 self.keep_temp_files = keep_temp_files | 348 self.keep_temp_files = keep_temp_files |
267 self.target_map = target_map | 349 self.target_map = target_map |
| 350 self.is_debug = False |
268 # detect bbs map if target_map is None. | 351 # detect bbs map if target_map is None. |
269 # NOTE when target_map == {}, that means "do not execute commands", | 352 # NOTE when target_map == {}, that means "do not execute commands", |
270 # different to default value. | 353 # different to default value. |
271 if isinstance(target_map, types.NoneType): | 354 if isinstance(target_map, types.NoneType): |
272 # generate default target map | 355 # generate default target map |
273 self.target_map = self.detect_target_map() | 356 self.target_map = self.detect_target_map() |
274 | 357 |
275 def _get_temp_filename(self, prefix): | 358 def _get_temp_filename(self, prefix): |
276 ''' (internal) Returns name of a temporary file in self.tmp_root ''' | 359 ''' (internal) Returns name of a temporary file in self.tmp_root ''' |
277 (fd, name) = tempfile.mkstemp(prefix=prefix, dir=self.tmp_root) | 360 (fd, name) = tempfile.mkstemp(prefix=prefix, dir=self.tmp_root) |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
335 """ | 418 """ |
336 Detects the target selection map. | 419 Detects the target selection map. |
337 Use machine architecture in current implementation. | 420 Use machine architecture in current implementation. |
338 """ | 421 """ |
339 arch = utils.get_arch() | 422 arch = utils.get_arch() |
340 for regex, target_map in DEFAULT_ARCH_TARGET_MAP.items(): | 423 for regex, target_map in DEFAULT_ARCH_TARGET_MAP.items(): |
341 if re.match(regex, arch): | 424 if re.match(regex, arch): |
342 return target_map | 425 return target_map |
343 raise TestError('INTERNAL ERROR: unknown architecture, need target_map') | 426 raise TestError('INTERNAL ERROR: unknown architecture, need target_map') |
344 | 427 |
345 def detect_layout(self, layout_desciption, size=None): | 428 def detect_layout(self, layout_desciption, size, image): |
346 """ | 429 """ |
347 Detects and builds layout according to current flash ROM size | 430 Detects and builds layout according to current flash ROM size |
348 and a simple layout description language. | 431 (or image) and a simple layout description language. |
349 If parameter 'size' is omitted, self.get_size() will be called. | |
350 | 432 |
351 See help(flashrom_util.compile_layout) for the syntax of description. | 433 NOTE: if you don't trust any available FMAP layout information in |
| 434 flashrom image, pass image = None. |
| 435 |
| 436 Args: |
| 437 layout_description: Pre-defined layout description. See |
| 438 help(flashrom_util.compile_layout) for syntax detail. |
| 439 size: Size of flashrom. If size is None, self.get_size() |
| 440 will be called. |
| 441 image: (optional) Flash ROM image that contains FMAP layout info. |
| 442 If image is None, layout will be calculated by size only. |
352 | 443 |
353 Returns the layout map (empty if any error). | 444 Returns the layout map (empty if any error). |
354 """ | 445 """ |
355 if not size: | 446 ret = None |
356 size = self.get_size() | 447 if image: |
357 return compile_layout(layout_desciption, size) | 448 if self.is_debug: |
| 449 print " * detect_layout: try FMAP" |
| 450 ret = decode_fmap_layout(DEFAULT_CHROMEOS_FMAP_CONVERSION, image) |
| 451 if not ret: |
| 452 if not size: |
| 453 size = self.get_size() |
| 454 ret = compile_layout(layout_desciption, size) |
| 455 if self.is_debug: |
| 456 print " * detect_layout: using pre-defined memory layout" |
| 457 elif self.is_debug: |
| 458 print " * detect_layout: using FMAP layout in firmware image." |
| 459 return ret |
358 | 460 |
359 def detect_chromeos_layout(self, target, size=None): | 461 def detect_chromeos_layout(self, target, size, image): |
360 """ | 462 """ |
361 Detects and builds ChromeOS firmware layout according to current flash | 463 Detects and builds ChromeOS firmware layout according to current flash |
362 ROM size. If parameter 'size' is None, self.get_size() will be called. | 464 ROM size. Currently supported targets are: 'bios' or 'ec'. |
363 | 465 |
364 Currently supported targets are: 'bios' or 'ec'. | 466 See help(flashrom_util.flashrom_util.detect_layout) for detail |
| 467 information of argument size and image. |
365 | 468 |
366 Returns the layout map (empty if any error). | 469 Returns the layout map (empty if any error). |
367 """ | 470 """ |
368 assert target in DEFAULT_CHROMEOS_FIRMWARE_LAYOUT_DESCRIPTIONS, \ | 471 assert target in DEFAULT_CHROMEOS_FIRMWARE_LAYOUT_DESCRIPTIONS, \ |
369 'unknown layout target: ' + test | 472 'unknown layout target: ' + test |
370 chromeos_target = DEFAULT_CHROMEOS_FIRMWARE_LAYOUT_DESCRIPTIONS[target] | 473 chromeos_target = DEFAULT_CHROMEOS_FIRMWARE_LAYOUT_DESCRIPTIONS[target] |
371 return self.detect_layout(chromeos_target, size) | 474 return self.detect_layout(chromeos_target, size, image) |
372 | 475 |
373 def detect_chromeos_bios_layout(self, size=None): | 476 def detect_chromeos_bios_layout(self, size, image): |
374 """ Detects standard ChromeOS BIOS layout """ | 477 """ Detects standard ChromeOS BIOS layout. |
375 return self.detect_chromeos_layout(self.TARGET_BIOS, size) | 478 A short cut to detect_chromeos_layout(TARGET_BIOS, size, image). """ |
| 479 return self.detect_chromeos_layout(self.TARGET_BIOS, size, image) |
376 | 480 |
377 def detect_chromeos_ec_layout(self, size=None): | 481 def detect_chromeos_ec_layout(self, size, image): |
378 """ Detects standard ChromeOS Embedded Controller layout """ | 482 """ Detects standard ChromeOS Embedded Controller layout. |
379 return self.detect_chromeos_layout(self.TARGET_EC, size) | 483 A short cut to detect_chromeos_layout(TARGET_EC, size, image). """ |
| 484 return self.detect_chromeos_layout(self.TARGET_EC, size, image) |
380 | 485 |
381 def read_whole(self): | 486 def read_whole(self): |
382 ''' | 487 ''' |
383 Reads whole flash ROM data. | 488 Reads whole flash ROM data. |
384 Returns the data read from flash ROM, or empty string for other error. | 489 Returns the data read from flash ROM, or empty string for other error. |
385 ''' | 490 ''' |
386 tmpfn = self._get_temp_filename('rd_') | 491 tmpfn = self._get_temp_filename('rd_') |
387 cmd = '%s"%s" -r "%s"' % (self.cmd_prefix, self.tool_path, tmpfn) | 492 cmd = '%s"%s" -r "%s"' % (self.cmd_prefix, self.tool_path, tmpfn) |
388 if self.verbose: | 493 if self.verbose: |
389 print 'flashrom_util.read_whole(): ', cmd | 494 print 'flashrom_util.read_whole(): ', cmd |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
543 | 648 |
544 TARGET_BIOS = DEFAULT_TARGET_NAME_BIOS | 649 TARGET_BIOS = DEFAULT_TARGET_NAME_BIOS |
545 TARGET_EC = DEFAULT_TARGET_NAME_EC | 650 TARGET_EC = DEFAULT_TARGET_NAME_EC |
546 | 651 |
547 def __init__(self, flashrom_util_instance=None, is_verbose=False): | 652 def __init__(self, flashrom_util_instance=None, is_verbose=False): |
548 """ | 653 """ |
549 Initializes internal variables and states. | 654 Initializes internal variables and states. |
550 | 655 |
551 Arguments: | 656 Arguments: |
552 flashrom_util_instance: An instance of existing flashrom_util. If | 657 flashrom_util_instance: An instance of existing flashrom_util. If |
553 not provided, FirmwareUpdater will create | 658 not provided, FlashromUtility will create |
554 one with all default values. | 659 one with all default values. |
555 is_verbose: Flag to control outputting verbose messages. | 660 is_verbose: Flag to control outputting verbose messages. |
556 """ | 661 """ |
557 self.flashrom = flashrom_util_instance | 662 self.flashrom = flashrom_util_instance |
558 if not self.flashrom: | 663 if not self.flashrom: |
559 self.flashrom = flashrom_util(verbose=is_verbose) | 664 self.flashrom = flashrom_util(verbose=is_verbose) |
560 self.current_image = None | 665 self.current_image = None |
561 self.layout = None | 666 self.layout = None |
562 self.whole_flash_layout = None | 667 self.whole_flash_layout = None |
563 self.skip_verify = None | 668 self.skip_verify = None |
564 self.change_history = [] | 669 self.change_history = [] |
565 self.is_verbose = is_verbose | 670 self.is_verbose = is_verbose |
566 self.is_debug = False | 671 self.is_debug = False |
567 | 672 |
568 def initialize(self, target, layout_desc=None, skip_verify=None): | 673 def initialize(self, target, layout_image=None, layout_desc=None, |
569 """ Starts flashrom initialization with given target. """ | 674 use_fmap_layout=True, skip_verify=None): |
| 675 """ |
| 676 Starts flashrom initialization with given target. |
| 677 |
| 678 Args: |
| 679 target: Name of the target you are dealing with (check TARGET_*) |
| 680 layout_desc: (optional) Description of pre-defined layout |
| 681 layout_image: (optional) A image blob containing FMAP for building |
| 682 layout. None if you want to use current system flash content |
| 683 use_fmap_layout: Use True (default) if you trust the FMAP in |
| 684 layout_image. |
| 685 skip_verify: Description of what data must be skipped when |
| 686 doing comparison / verification. |
| 687 """ |
570 flashrom = self.flashrom | 688 flashrom = self.flashrom |
571 if not flashrom.select_target(target): | 689 if not flashrom.select_target(target): |
572 raise TestError("Cannot Select Target. Abort.") | 690 raise TestError("Cannot Select Target. Abort.") |
| 691 |
573 if self.is_verbose: | 692 if self.is_verbose: |
574 print " - reading current content" | 693 print " - reading current content" |
575 self.current_image = flashrom.read_whole() | 694 self.current_image = flashrom.read_whole() |
576 if not self.current_image: | 695 if not self.current_image: |
577 raise TestError("Cannot read flashrom image. Abort.") | 696 raise TestError("Cannot read flashrom image. Abort.") |
578 flashrom_size = len(self.current_image) | 697 flashrom_size = len(self.current_image) |
| 698 |
| 699 if not use_fmap_layout: |
| 700 layout_image = None |
| 701 elif not layout_image: |
| 702 layout_image = current_image |
| 703 |
579 if layout_desc: | 704 if layout_desc: |
580 layout = flashrom.detect_layout(layout_desc, flashrom_size) | 705 layout = flashrom.detect_layout( |
| 706 layout_desc, flashrom_size, layout_image) |
581 else: | 707 else: |
582 layout = flashrom.detect_chromeos_layout(target, flashrom_size) | 708 layout = flashrom.detect_chromeos_layout( |
| 709 target, flashrom_size, layout_image) |
583 self.layout = layout | 710 self.layout = layout |
584 self.whole_flash_layout = flashrom.detect_layout('all', flashrom_size) | 711 self.whole_flash_layout = flashrom.detect_layout('all', flashrom_size) |
585 if not skip_verify: | 712 if not skip_verify: |
586 skip_verify = DEFAULT_CHROMEOS_FIRMWARE_SKIP_VERIFY_LIST[target] | 713 skip_verify = DEFAULT_CHROMEOS_FIRMWARE_SKIP_VERIFY_LIST[target] |
587 self.skip_verify = skip_verify | 714 self.skip_verify = skip_verify |
588 self.change_history = [] # reset list | 715 self.change_history = [] # reset list |
589 | 716 |
590 def get_current_image(self): | 717 def get_current_image(self): |
591 """ Returns current flashrom image (physically, not changed) """ | 718 """ Returns current flashrom image (physically, not changed) """ |
592 return self.current_image | 719 return self.current_image |
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
780 except ImportError: | 907 except ImportError: |
781 # print 'using mocks' | 908 # print 'using mocks' |
782 utils = mock_utils() | 909 utils = mock_utils() |
783 TestError = mock_TestError | 910 TestError = mock_TestError |
784 | 911 |
785 | 912 |
786 # main stub | 913 # main stub |
787 if __name__ == "__main__": | 914 if __name__ == "__main__": |
788 # TODO(hungte) provide unit tests or command line usage | 915 # TODO(hungte) provide unit tests or command line usage |
789 pass | 916 pass |
OLD | NEW |