| 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 """ This module provides convenience routines to access Flash ROM (EEPROM) | 6 """ This module provides convenience routines to access Flash ROM (EEPROM) |
| 7 | 7 |
| 8 flashrom_util is based on utility 'flashrom'. | 8 flashrom_util is based on utility 'flashrom'. |
| 9 | 9 |
| 10 Original tool syntax: | 10 Original tool syntax: |
| 11 (read ) flashrom -r <file> | 11 (read ) flashrom -r <file> |
| 12 (write) flashrom -l <layout_fn> [-i <image_name> ...] -w <file> | 12 (write) flashrom -l <layout_fn> [-i <image_name> ...] -w <file> |
| 13 | 13 |
| 14 The layout_fn is in format of | 14 The layout_fn is in format of |
| 15 address_begin:address_end image_name | 15 address_begin:address_end image_name |
| 16 which defines a region between (address_begin, address_end) and can | 16 which defines a region between (address_begin, address_end) and can |
| 17 be accessed by the name image_name. | 17 be accessed by the name image_name. |
| 18 | 18 |
| 19 Currently the tool supports multiple partial write but not partial read. | 19 Currently the tool supports multiple partial write but not partial read. |
| 20 | 20 |
| 21 In the flashrom_util, we provide read and partial write abilities. | 21 In the flashrom_util, we provide read and partial write abilities. |
| 22 For more information, see help(flashrom_util.flashrom_util). | 22 For more information, see help(flashrom_util.flashrom_util). |
| 23 """ | 23 """ |
| 24 | 24 |
| 25 import os | 25 import os |
| 26 import re | 26 import re |
| 27 import stat |
| 27 import subprocess | 28 import subprocess |
| 28 import sys | 29 import sys |
| 29 import tempfile | 30 import tempfile |
| 30 import types | 31 import types |
| 31 | 32 |
| 33 import chromeos_interface |
| 32 | 34 |
| 33 # simple layout description language compiler | 35 class TestError(Exception): |
| 34 def compile_layout(desc, size): | 36 pass |
| 35 """ compile_layout(desc, size) -> layout | |
| 36 | 37 |
| 37 Compiles a flashrom layout by simple description language. | |
| 38 Returns the result as a map. Empty map for any error. | |
| 39 | 38 |
| 40 syntax: <desc> ::= <partitions> | 39 class LayoutScraper(object): |
| 41 <partitions> ::= <partition> | 40 '''Object of this class is used to retrieve layout from a BIOS file.''' |
| 42 | <partitions> '|' <partition> | |
| 43 <partition> ::= <spare_section> | |
| 44 | <partition> ',' <section> | |
| 45 | <section> ',' <partition> | |
| 46 <section> ::= <name> '=' <size> | |
| 47 <spare_section> ::= '*' | |
| 48 | <name> | |
| 49 | <name> '=' '*' | |
| 50 | 41 |
| 51 * Example: 'ro|rw', 'ro=0x1000,*|*,rw=0x1000' | 42 # The default conversion table for mosys. |
| 52 * Each partition share same space from total size of flashrom. | 43 DEFAULT_CHROMEOS_FMAP_CONVERSION = { |
| 53 * Sections are fix sized, or "spare" which consumes all remaining | 44 "Boot Stub": "FV_BSTUB", |
| 54 space from a partition. | 45 "GBB Area": "FV_GBB", |
| 55 * You can use any non-zero decimal or heximal (0xXXXX) in <size>. | 46 "Recovery Firmware": "FVDEV", |
| 56 (size as zero is reserved now) | 47 "RO VPD": "RO_VPD", |
| 57 * You can use '*' as <name> for "unamed" items which will be ignored in | 48 "Firmware A Key": "VBOOTA", |
| 58 final layout output. | 49 "Firmware A Data": "FVMAIN", |
| 59 * You can use "<name>=*" or simply "<name>" (including '*', the | 50 "Firmware B Key": "VBOOTB", |
| 60 'unamed section') to define spare section. | 51 "Firmware B Data": "FVMAINB", |
| 61 * There must be always one (no more, no less) spare section in | 52 "Log Volume": "FV_LOG", |
| 62 each partition. | 53 } |
| 63 """ | |
| 64 # create an empty layout first | |
| 65 layout = {} | |
| 66 err_ret = {} | |
| 67 | 54 |
| 68 # prepare: remove all spaces (literal from string.whitespace) | 55 def __init__(self, os_if): |
| 69 desc = ''.join([c for c in desc if c not in '\t\n\x0b\x0c\r ']) | 56 self.image = None |
| 70 # find equally-sized partitions | 57 self.os_if = os_if |
| 71 parts = desc.split('|') | |
| 72 block_size = size / len(parts) | |
| 73 offset = 0 | |
| 74 | 58 |
| 75 for part in parts: | 59 def _get_text_layout(self, file_name): |
| 76 sections = part.split(',') | 60 '''Retrieve text layout from a firmware image file. |
| 77 sizes = [] | |
| 78 names = [] | |
| 79 spares = 0 | |
| 80 | 61 |
| 81 for section in sections: | 62 This function uses the 'mosys' utility to scan the firmware image and |
| 82 # skip empty section to allow final ',' | 63 retrieve the section layout information. |
| 83 if section == '': | 64 |
| 65 The layout is reported as a set of lines with multiple |
| 66 "<name>"="value" pairs, all this output is passed to the caller. |
| 67 ''' |
| 68 |
| 69 mosys_cmd = 'mosys -k eeprom map %s' % file_name |
| 70 return self.os_if.run_shell_command_get_output(mosys_cmd) |
| 71 |
| 72 def _line_to_dictionary(self, line): |
| 73 '''Convert a text layout line into a dictionary. |
| 74 |
| 75 Get a string consisting of single space separated "<name>"="value>" |
| 76 pairs and convert it into a dictionary where keys are the <name> |
| 77 fields, and values are the corresponding <value> fields. |
| 78 |
| 79 Return the dictionary to the caller. |
| 80 ''' |
| 81 |
| 82 rv = {} |
| 83 |
| 84 items = line.replace('" ', '"^').split('^') |
| 85 for item in items: |
| 86 pieces = item.split('=') |
| 87 if len(pieces) != 2: |
| 84 continue | 88 continue |
| 85 # format name=v or name ? | 89 rv[pieces[0]] = pieces[1].strip('"') |
| 86 if section.find('=') >= 0: | 90 return rv |
| 87 k, v = section.split('=') | |
| 88 if v == '*': | |
| 89 v = 0 # spare section | |
| 90 else: | |
| 91 v = int(v, 0) | |
| 92 if v == 0: | |
| 93 raise TestError('Using size as 0 is prohibited now.') | |
| 94 else: | |
| 95 k, v = (section, 0) # spare, should appear for only one. | |
| 96 if v == 0: | |
| 97 spares = spares + 1 | |
| 98 names.append(k) | |
| 99 sizes.append(v) | |
| 100 | 91 |
| 101 if spares != 1: | 92 def check_layout(self, layout, file_size): |
| 102 # each partition should have exactly one spare field | 93 '''Verify the layout to be consistent. |
| 103 return err_ret | |
| 104 | 94 |
| 105 spare_size = block_size - sum(sizes) | 95 The layout is consistent if there is no overlapping sections and the |
| 106 sizes[sizes.index(0)] = spare_size | 96 section boundaries do not exceed the file size. |
| 107 # fill sections | |
| 108 for i in range(len(names)): | |
| 109 # ignore unamed sections | |
| 110 if names[i] != '*': | |
| 111 layout[names[i]] = (offset, offset + sizes[i] - 1) | |
| 112 offset = offset + sizes[i] | |
| 113 | 97 |
| 114 return layout | 98 Inputs: |
| 99 layout: a dictionary keyed by a string (the section name) with |
| 100 values being two integers tuples, the first and the last |
| 101 bites' offset in the file. |
| 102 file_size: and integer, the size of the file the layout describes |
| 103 the sections in. |
| 115 | 104 |
| 105 Raises: |
| 106 TestError in case the layout is not consistent. |
| 107 ''' |
| 108 |
| 109 # Generate a list of section range tuples. |
| 110 ost = sorted([layout[section] for section in layout]) |
| 111 base = 0 |
| 112 for section_base, section_end in ost: |
| 113 if section_base < base or section_end < section_base: |
| 114 raise TestError('bad section at 0x%x..0x%x' % ( |
| 115 section_base, section_end)) |
| 116 base = section_end |
| 117 if base > file_size: |
| 118 raise TestError('Section end 0x%x exceeds file size %x' % ( |
| 119 base, file_size)) |
| 120 |
| 121 def get_layout(self, file_name): |
| 122 '''Generate layout for a firmware file. |
| 123 |
| 124 First retrieve the text layout as reported by 'mosys' and then convert |
| 125 it into a dictionary, replacing section names reported by mosys into |
| 126 matching names from DEFAULT_CHROMEOS_FMAP_CONVERSION dictionary above, |
| 127 using the names as keys in the layout dictionary. The elements of the |
| 128 layout dictionary are the offsets of the first ans last bytes of the |
| 129 section in the firmware file. |
| 130 |
| 131 Then verify the generated layout's consistency and return it to the |
| 132 caller. |
| 133 ''' |
| 134 |
| 135 layout_data = {} # keyed by the section name, elements - tuples of |
| 136 # (<section start addr>, <section end addr>) |
| 137 |
| 138 for line in self._get_text_layout(file_name): |
| 139 d = self._line_to_dictionary(line) |
| 140 try: |
| 141 name = self.DEFAULT_CHROMEOS_FMAP_CONVERSION[d['area_name']] |
| 142 except KeyError: |
| 143 continue # This line does not contain an area of interest. |
| 144 |
| 145 if name in layout_data: |
| 146 raise TestError('%s duplicated in the layout' % area_name) |
| 147 |
| 148 offset = int(d['area_offset'], 0) |
| 149 size = int(d['area_size'], 0) |
| 150 layout_data[name] = (offset, offset + size - 1) |
| 151 |
| 152 self.check_layout(layout_data, os.stat(file_name)[stat.ST_SIZE]) |
| 153 return layout_data |
| 116 | 154 |
| 117 # flashrom utility wrapper | 155 # flashrom utility wrapper |
| 118 class flashrom_util(object): | 156 class flashrom_util(object): |
| 119 """ a wrapper for "flashrom" utility. | 157 """ a wrapper for "flashrom" utility. |
| 120 | 158 |
| 121 You can read, write, or query flash ROM size with this utility. | 159 You can read, write, or query flash ROM size with this utility. |
| 122 Although you can do "partial-write", the tools always takes a | 160 Although you can do "partial-write", the tools always takes a |
| 123 full ROM image as input parameter. | 161 full ROM image as input parameter. |
| 124 | 162 |
| 125 NOTE before accessing flash ROM, you may need to first "select" | 163 NOTE before accessing flash ROM, you may need to first "select" |
| 126 your target - usually BIOS or EC. That part is not handled by | 164 your target - usually BIOS or EC. That part is not handled by |
| 127 this utility. Please find other external script to do it. | 165 this utility. Please find other external script to do it. |
| 128 | 166 |
| 129 To perform a read, you need to: | 167 To perform a read, you need to: |
| 130 1. Prepare a flashrom_util object | 168 1. Prepare a flashrom_util object |
| 131 ex: flashrom = flashrom_util.flashrom_util() | 169 ex: flashrom = flashrom_util.flashrom_util() |
| 132 2. Decide target (BIOS/EC) | 170 2. Perform read operation |
| 133 ex: flashrom.select_bios_flashrom() | |
| 134 3. Perform read operation | |
| 135 ex: image = flashrom.read_whole() | 171 ex: image = flashrom.read_whole() |
| 136 | 172 |
| 137 To perform a (partial) write, you need to: | 173 When the contents of the flashrom is read off the target, it's map |
| 138 1. Select target (BIOS/EC) | 174 gets created automatically (read from the flashrom image using |
| 139 ex: flashrom.select_ec_flashrom() | 175 'mosys'). If the user wants this object to operate on some other file, |
| 140 2. Create or load a layout map (see explain of layout below) | 176 he could either have the map for the file created explicitly by |
| 141 ex: layout_map = { 'all': (0, rom_size - 1) } | 177 invoking flashrom.set_bios_layout(filename), or supply his own map |
| 178 (which is a dictionary where keys are section names, and values are |
| 179 tuples of integers, base address of the section and the last address |
| 180 of the section). |
| 181 |
| 182 By default this object operates on the map retrieved from the image and |
| 183 stored locally, this map can be overwritten by an explicitly passed user |
| 184 map. |
| 185 |
| 186 To perform a (partial) write: |
| 187 |
| 188 1. Prepare a buffer storing an image to be written into the flashrom. |
| 189 2. Have the map generated automatically or prepare your own, for instance: |
| 190 ex: layout_map_all = { 'all': (0, rom_size - 1) } |
| 142 ex: layout_map = { 'ro': (0, 0xFFF), 'rw': (0x1000, rom_size-1) } | 191 ex: layout_map = { 'ro': (0, 0xFFF), 'rw': (0x1000, rom_size-1) } |
| 143 You can also use built-in layout like detect_chromeos_bios_layout(), | 192 4. Perform write operation |
| 144 detect_chromeos_layout(), or detect_layout() to build the layout maps. | |
| 145 3. Prepare a full base image | |
| 146 ex: image = flashrom.read_whole() | |
| 147 ex: image = chr(0xFF) * rom_size | |
| 148 4. (optional) Modify data in base image | |
| 149 ex: new_image = flashrom.put_section(image, layout_map, 'all', mydata) | |
| 150 5. Perform write operation | |
| 151 ex: flashrom.write_partial(new_image, layout_map, ('all',)) | |
| 152 | 193 |
| 153 P.S: you can also create the new_image in your own way, for example: | 194 ex using default map: |
| 154 rom_size = flashrom_util.get_size() | 195 flashrom.write_partial(new_image, (<section_name>, ...)) |
| 155 erase_image = chr(0xFF) * rom_size | 196 ex using explicitly provided map: |
| 156 flashrom.write_partial(erase_image, layout_map, ('all',)) | 197 flashrom.write_partial(new_image, layout_map_all, ('all',)) |
| 157 | |
| 158 The layout is a dictionary of { 'name': (address_begin, addres_end) }. | |
| 159 Note that address_end IS included in the range. | |
| 160 See help(detect_layout) for easier way to generate layout maps. | |
| 161 | 198 |
| 162 Attributes: | 199 Attributes: |
| 163 tool_path: file path to the tool 'flashrom' | |
| 164 cmd_prefix: prefix of every shell cmd, ex: "PATH=.:$PATH;export PATH;" | |
| 165 tmp_root: a folder name for mkstemp (for temp of layout and images) | |
| 166 verbose: print debug and helpful messages | 200 verbose: print debug and helpful messages |
| 167 keep_temp_files: boolean flag to control cleaning of temporary files | 201 keep_temp_files: boolean flag to control cleaning of temporary files |
| 168 target_map: map of what commands should be invoked to switch targets. | |
| 169 if you don't need any commands, use empty dict {}. | |
| 170 if you want default detection, use None (default param). | |
| 171 """ | 202 """ |
| 172 | 203 |
| 173 # default target selection commands, by machine architecture | 204 def __init__(self, verbose=False, keep_temp_files=False): |
| 174 # syntax: { 'arch_regex': exec_script, ... } | |
| 175 default_arch_target_map = { | |
| 176 '^x86|^i\d86': { | |
| 177 # The magic numbers here are register indexes and values that apply | |
| 178 # to all current known x86 based ChromeOS devices. | |
| 179 # Detail information is defined in section #"10.1.50 GCS-General | |
| 180 # Control and Status Register" of document "Intel NM10 Express | |
| 181 # Chipsets". | |
| 182 "bios": 'iotools mmio_write32 0xfed1f410 ' + | |
| 183 '`iotools mmio_read32 0xfed1f410 |head -c 6`0460', | |
| 184 "ec": 'iotools mmio_write32 0xfed1f410 ' + | |
| 185 '`iotools mmio_read32 0xfed1f410 |head -c 6`0c60', | |
| 186 }, | |
| 187 } | |
| 188 | |
| 189 default_chromeos_layout_desc = { | |
| 190 "bios": """ | |
| 191 FV_LOG = 0x20000, | |
| 192 NV_COMMON_STORE = 0x10000, | |
| 193 VBOOTA = 0x02000, | |
| 194 FVMAIN = 0xB0000, | |
| 195 VBOOTB = 0x02000, | |
| 196 FVMAINB = 0xB0000, | |
| 197 NVSTORAGE = 0x10000, | |
| 198 FV_RW_RESERVED = *, | |
| 199 | | |
| 200 FV_RO_RESERVED = *, | |
| 201 FVDEV = 0xB0000, | |
| 202 FV_GBB = 0x20000, | |
| 203 FV_BSTUB = 0x40000, | |
| 204 """, | |
| 205 "ec": """ | |
| 206 EC_RO | |
| 207 | | |
| 208 EC_RW | |
| 209 """, | |
| 210 } | |
| 211 | |
| 212 def __init__(self, | |
| 213 tool_path='/usr/sbin/flashrom', | |
| 214 cmd_prefix='', | |
| 215 tmp_root=None, | |
| 216 verbose=False, | |
| 217 keep_temp_files=False, | |
| 218 target_map=None): | |
| 219 """ constructor of flashrom_util. help(flashrom_util) for more info """ | 205 """ constructor of flashrom_util. help(flashrom_util) for more info """ |
| 220 self.tool_path = tool_path | |
| 221 self.cmd_prefix = cmd_prefix | |
| 222 self.tmp_root = tmp_root | |
| 223 self.verbose = verbose | 206 self.verbose = verbose |
| 224 self.keep_temp_files = keep_temp_files | 207 self.keep_temp_files = keep_temp_files |
| 225 self.target_map = target_map | 208 self.bios_layout = {} |
| 226 # detect bbs map if target_map is None. | 209 self.os_if = chromeos_interface.ChromeOSInterface(True) |
| 227 # NOTE when target_map == {}, that means "do not execute commands", | 210 self.os_if.init(tempfile.gettempdir()) |
| 228 # different to default value. | 211 self._enable_bios_access() |
| 229 if isinstance(target_map, types.NoneType): | 212 |
| 230 # generate default target map | 213 def _enable_bios_access(self): |
| 231 self.target_map = self.detect_target_map() | 214 if not self.os_if.target_hosted(): |
| 215 return |
| 216 value = int(self.os_if.run_shell_command_get_output( |
| 217 'iotools mmio_read32 0xfed1f410')[0], 0) |
| 218 value = (value & 0xffff0000) + 0x460 |
| 219 self.os_if.run_shell_command( |
| 220 'iotools mmio_write32 0xfed1f410 0x%x' % value) |
| 232 | 221 |
| 233 def get_temp_filename(self, prefix): | 222 def get_temp_filename(self, prefix): |
| 234 ''' (internal) Returns name of a temporary file in self.tmp_root ''' | 223 ''' (internal) Returns name of a temporary file in self.tmp_root ''' |
| 235 (fd, name) = tempfile.mkstemp(prefix=prefix, dir=self.tmp_root) | 224 (fd, name) = tempfile.mkstemp(prefix=prefix) |
| 236 os.close(fd) | 225 os.close(fd) |
| 237 return name | 226 return name |
| 238 | 227 |
| 239 def remove_temp_file(self, filename): | 228 def remove_temp_file(self, filename): |
| 240 """ (internal) Removes a temp file if self.keep_temp_files is false. """ | 229 """ (internal) Removes a temp file if self.keep_temp_files is false. """ |
| 241 if self.keep_temp_files: | 230 if self.keep_temp_files: |
| 242 return | 231 return |
| 243 if os.path.exists(filename): | 232 if os.path.exists(filename): |
| 244 os.remove(filename) | 233 os.remove(filename) |
| 245 | 234 |
| 246 def create_layout_file(self, layout_map): | 235 def create_layout_file(self, layout_map): |
| 247 ''' | 236 ''' |
| 248 (internal) Creates a layout file based on layout_map. | 237 (internal) Creates a layout file based on layout_map. |
| 249 Returns the file name containing layout information. | 238 Returns the file name containing layout information. |
| 250 ''' | 239 ''' |
| 251 layout_text = ['0x%08lX:0x%08lX %s' % (v[0], v[1], k) | 240 layout_text = ['0x%08lX:0x%08lX %s' % (v[0], v[1], k) |
| 252 for k, v in layout_map.items()] | 241 for k, v in layout_map.items()] |
| 253 layout_text.sort() # XXX unstable if range exceeds 2^32 | 242 layout_text.sort() # XXX unstable if range exceeds 2^32 |
| 254 tmpfn = self.get_temp_filename('lay') | 243 tmpfn = self.get_temp_filename('lay') |
| 255 open(tmpfn, 'wb').write('\n'.join(layout_text) + '\n') | 244 open(tmpfn, 'wb').write('\n'.join(layout_text) + '\n') |
| 256 return tmpfn | 245 return tmpfn |
| 257 | 246 |
| 258 def get_section(self, base_image, layout_map, section_name): | 247 def get_section(self, base_image, section_name): |
| 259 ''' | 248 ''' |
| 260 Retrieves a section of data based on section_name in layout_map. | 249 Retrieves a section of data based on section_name in layout_map. |
| 261 Raises error if unknown section or invalid layout_map. | 250 Raises error if unknown section or invalid layout_map. |
| 262 ''' | 251 ''' |
| 263 pos = layout_map[section_name] | 252 pos = self.bios_layout[section_name] |
| 264 if pos[0] >= pos[1] or pos[1] >= len(base_image): | 253 if pos[0] >= pos[1] or pos[1] >= len(base_image): |
| 265 raise TestError('INTERNAL ERROR: invalid layout map: %s.' % | 254 raise TestError('INTERNAL ERROR: invalid layout map: %s.' % |
| 266 section_name) | 255 section_name) |
| 267 return base_image[pos[0] : pos[1] + 1] | 256 return base_image[pos[0] : pos[1] + 1] |
| 268 | 257 |
| 269 def put_section(self, base_image, layout_map, section_name, data): | 258 def put_section(self, base_image, section_name, data): |
| 270 ''' | 259 ''' |
| 271 Updates a section of data based on section_name in layout_map. | 260 Updates a section of data based on section_name in bios_layout. |
| 272 Raises error if unknown section or invalid layout_map. | 261 Raises error if unknown section. |
| 273 Returns the full updated image data. | 262 Returns the full updated image data. |
| 274 ''' | 263 ''' |
| 275 pos = layout_map[section_name] | 264 pos = self.bios_layout[section_name] |
| 276 if pos[0] >= pos[1] or pos[1] >= len(base_image): | 265 if pos[0] >= pos[1] or pos[1] >= len(base_image): |
| 277 raise TestError('INTERNAL ERROR: invalid layout map.') | 266 raise TestError('INTERNAL ERROR: invalid layout map.') |
| 278 if len(data) != pos[1] - pos[0] + 1: | 267 if len(data) != pos[1] - pos[0] + 1: |
| 279 raise TestError('INTERNAL ERROR: unmatched data size.') | 268 raise TestError('INTERNAL ERROR: unmatched data size.') |
| 280 return base_image[0 : pos[0]] + data + base_image[pos[1] + 1 :] | 269 return base_image[0 : pos[0]] + data + base_image[pos[1] + 1 :] |
| 281 | 270 |
| 282 def get_size(self): | 271 def get_size(self): |
| 283 """ Gets size of current flash ROM """ | 272 """ Gets size of current flash ROM """ |
| 284 # TODO(hungte) Newer version of tool (flashrom) may support --get-size | 273 # TODO(hungte) Newer version of tool (flashrom) may support --get-size |
| 285 # command which is faster in future. Right now we use back-compatible | 274 # command which is faster in future. Right now we use back-compatible |
| 286 # method: read whole and then get length. | 275 # method: read whole and then get length. |
| 287 image = self.read_whole() | 276 image = self.read_whole() |
| 288 return len(image) | 277 return len(image) |
| 289 | 278 |
| 290 def detect_target_map(self): | 279 def set_bios_layout(self, file_name): |
| 291 """ | 280 """get layout read from the BIOS """ |
| 292 Detects the target selection map. | |
| 293 Use machine architecture in current implementation. | |
| 294 """ | |
| 295 arch = utils.get_arch() | |
| 296 for regex, target_map in self.default_arch_target_map.items(): | |
| 297 if re.match(regex, arch): | |
| 298 return target_map | |
| 299 raise TestError('INTERNAL ERROR: unknown architecture, need target_map') | |
| 300 | 281 |
| 301 def detect_layout(self, layout_desciption, size=None): | 282 scraper = LayoutScraper(self.os_if) |
| 302 """ | 283 self.bios_layout = scraper.get_layout(file_name) |
| 303 Detects and builds layout according to current flash ROM size | |
| 304 and a simple layout description language. | |
| 305 If parameter 'size' is omitted, self.get_size() will be called. | |
| 306 | |
| 307 See help(flashrom_util.compile_layout) for the syntax of description. | |
| 308 | |
| 309 Returns the layout map (empty if any error). | |
| 310 """ | |
| 311 if not size: | |
| 312 size = self.get_size() | |
| 313 return compile_layout(layout_desciption, size) | |
| 314 | |
| 315 def detect_chromeos_layout(self, target, size=None): | |
| 316 """ | |
| 317 Detects and builds ChromeOS firmware layout according to current flash | |
| 318 ROM size. If parameter 'size' is None, self.get_size() will be called. | |
| 319 | |
| 320 Currently supported targets are: 'bios' or 'ec'. | |
| 321 | |
| 322 Returns the layout map (empty if any error). | |
| 323 """ | |
| 324 if target not in self.default_chromeos_layout_desc: | |
| 325 raise TestError('INTERNAL ERROR: unknown layout target: %s' % test) | |
| 326 chromeos_target = self.default_chromeos_layout_desc[target] | |
| 327 return self.detect_layout(chromeos_target, size) | |
| 328 | |
| 329 def detect_chromeos_bios_layout(self, size=None): | |
| 330 """ Detects standard ChromeOS BIOS layout """ | |
| 331 return self.detect_chromeos_layout('bios', size) | |
| 332 | |
| 333 def detect_chromeos_ec_layout(self, size=None): | |
| 334 """ Detects standard ChromeOS Embedded Controller layout """ | |
| 335 return self.detect_chromeos_layout('ec', size) | |
| 336 | 284 |
| 337 def read_whole(self): | 285 def read_whole(self): |
| 338 ''' | 286 ''' |
| 339 Reads whole flash ROM data. | 287 Reads whole flash ROM data. |
| 340 Returns the data read from flash ROM, or empty string for other error. | 288 Returns the data read from flash ROM, or empty string for other error. |
| 341 ''' | 289 ''' |
| 342 tmpfn = self.get_temp_filename('rd_') | 290 tmpfn = self.get_temp_filename('rd_') |
| 343 cmd = '%s"%s" -r "%s"' % (self.cmd_prefix, self.tool_path, tmpfn) | 291 cmd = 'flashrom -r "%s"' % (tmpfn) |
| 344 if self.verbose: | 292 if self.verbose: |
| 345 print 'flashrom_util.read_whole(): ', cmd | 293 print 'flashrom_util.read_whole(): ', cmd |
| 346 result = '' | |
| 347 | 294 |
| 348 if utils.system(cmd, ignore_status=True) == 0: # failure for non-zero | 295 self.os_if.run_shell_command(cmd) |
| 349 try: | 296 result = open(tmpfn, 'rb').read() |
| 350 result = open(tmpfn, 'rb').read() | 297 self.set_bios_layout(tmpfn) |
| 351 except IOError: | |
| 352 result = '' | |
| 353 | 298 |
| 354 # clean temporary resources | 299 # clean temporary resources |
| 355 self.remove_temp_file(tmpfn) | 300 self.remove_temp_file(tmpfn) |
| 356 return result | 301 return result |
| 357 | 302 |
| 358 def write_partial(self, base_image, layout_map, write_list): | 303 def write_partial(self, base_image, write_list, write_layout_map=None): |
| 359 ''' | 304 ''' |
| 360 Writes data in sections of write_list to flash ROM. | 305 Writes data in sections of write_list to flash ROM. |
| 361 Returns True on success, otherwise False. | 306 An exception is raised if write operation fails. |
| 362 ''' | 307 ''' |
| 308 |
| 309 if write_layout_map: |
| 310 layout_map = write_layout_map |
| 311 else: |
| 312 layout_map = self.bios_layout |
| 313 |
| 363 tmpfn = self.get_temp_filename('wr_') | 314 tmpfn = self.get_temp_filename('wr_') |
| 364 open(tmpfn, 'wb').write(base_image) | 315 open(tmpfn, 'wb').write(base_image) |
| 365 layout_fn = self.create_layout_file(layout_map) | 316 layout_fn = self.create_layout_file(layout_map) |
| 366 | 317 |
| 367 cmd = '%s"%s" -l "%s" -i %s -w "%s"' % ( | 318 cmd = 'flashrom -l "%s" -i %s -w "%s"' % ( |
| 368 self.cmd_prefix, self.tool_path, | |
| 369 layout_fn, ' -i '.join(write_list), tmpfn) | 319 layout_fn, ' -i '.join(write_list), tmpfn) |
| 370 if self.verbose: | 320 if self.verbose: |
| 371 print 'flashrom.write_partial(): ', cmd | 321 print 'flashrom.write_partial(): ', cmd |
| 372 result = False | |
| 373 | 322 |
| 374 if utils.system(cmd, ignore_status=True) == 0: # failure for non-zero | 323 self.os_if.run_shell_command(cmd) |
| 375 result = True | |
| 376 | 324 |
| 377 # clean temporary resources | 325 # clean temporary resources |
| 378 self.remove_temp_file(tmpfn) | 326 self.remove_temp_file(tmpfn) |
| 379 self.remove_temp_file(layout_fn) | 327 self.remove_temp_file(layout_fn) |
| 380 return result | |
| 381 | 328 |
| 382 def select_target(self, target): | 329 def write_whole(self, base_image): |
| 383 ''' | 330 '''Write the whole base image. ''' |
| 384 Selects (usually by setting BBS register) a target defined in target_map | 331 layout_map = { 'all': (0, len(base_image) - 1) } |
| 385 and then directs all further firmware access to certain region. | 332 self.write_partial(base_image, ('all',), layout_map) |
| 386 ''' | |
| 387 if target not in self.target_map: | |
| 388 return True | |
| 389 if self.verbose: | |
| 390 print 'flashrom.select_target("%s"): %s' % (target, | |
| 391 self.target_map[target]) | |
| 392 if utils.system(self.cmd_prefix + self.target_map[target], | |
| 393 ignore_status=True) == 0: | |
| 394 return True | |
| 395 return False | |
| 396 | |
| 397 def select_bios_flashrom(self): | |
| 398 ''' Directs all further accesses to BIOS flash ROM. ''' | |
| 399 return self.select_target('bios') | |
| 400 | |
| 401 def select_ec_flashrom(self): | |
| 402 ''' Directs all further accesses to Embedded Controller flash ROM. ''' | |
| 403 return self.select_target('ec') | |
| 404 | |
| 405 | |
| 406 # --------------------------------------------------------------------------- | |
| 407 # The flashrom_util supports both running inside and outside 'autotest' | |
| 408 # framework, so we need to provide some mocks and dynamically load | |
| 409 # autotest components here. | |
| 410 | |
| 411 | |
| 412 class mock_TestError(object): | |
| 413 """ a mock for error.TestError """ | |
| 414 def __init__(self, msg): | |
| 415 print msg | |
| 416 sys.exit(1) | |
| 417 | |
| 418 | |
| 419 class mock_utils(object): | |
| 420 """ a mock for autotest_li.client.bin.utils """ | |
| 421 def get_arch(self): | |
| 422 arch = os.popen('uname -m').read().rstrip() | |
| 423 arch = re.sub(r"i\d86", r"i386", arch, 1) | |
| 424 return arch | |
| 425 | |
| 426 def system(self, cmd, ignore_status=False): | |
| 427 p = subprocess.Popen(cmd, shell=True, | |
| 428 stdout=subprocess.PIPE, | |
| 429 stderr=subprocess.PIPE) | |
| 430 p.wait() | |
| 431 if p.returncode: | |
| 432 print p.stdout.read() | |
| 433 if not ignore_status: | |
| 434 raise TestError("failed to execute: %s\nError messages: %s" % ( | |
| 435 cmd, p.stderr.read())) | |
| 436 return p.returncode | |
| 437 | |
| 438 | |
| 439 # import autotest or mock utilities | |
| 440 try: | |
| 441 # print 'using autotest' | |
| 442 from autotest_lib.client.bin import test, utils | |
| 443 from autotest_lib.client.common_lib.error import TestError | |
| 444 except ImportError: | |
| 445 # print 'using mocks' | |
| 446 utils = mock_utils() | |
| 447 TestError = mock_TestError | |
| 448 | |
| 449 | |
| 450 # main stub | |
| 451 if __name__ == "__main__": | |
| 452 # TODO(hungte) provide unit tests or command line usage | |
| 453 pass | |
| OLD | NEW |