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 |