blob: 68cbb59c6fc89691a7b17caea963318be0cf1d45 [file] [log] [blame]
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +08001# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +08005"""A module to provide interface to ChromeOS services."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +08006
7import datetime
8import os
9import re
10import shutil
11import struct
12import subprocess
13import tempfile
14import time
15
16class ChromeOSInterfaceError(Exception):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +080017 """ChromeOS interface specific exception."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080018 pass
19
20class Crossystem(object):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +080021 """A wrapper for the crossystem utility."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080022
23 # Code dedicated for user triggering recovery mode through crossystem.
24 USER_RECOVERY_REQUEST_CODE = '193'
25
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +080026 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080027 The first three legacy boot vector digits are the boot vector base (the
28 entire vector consists of 5 digits). They used to be reported by the BIOS
29 through ACPI, but that scheme has been superseded by the 'crossystem'
30 interface.
31
32 The digits of the boot vector base have the following significance
33
34 - first digit -
35 1 - normal boot
36 2 - developer mode boot
37 3 - recovery initialed by pressing the recovery button
38 4 - recovery from developer mode warning screen
39 5 - recovery caused by both firmware images being invalid
40 6 - recovery caused by both kernel images being invalid
41 8 - recovery initiated by user
42
43 - second digit -
44 0 - recovery firmware
45 1 - rewritable firmware A
46 2 - rewritable firmware B
47
48 - third digit -
49 0 - Read only (recovery) EC firmware
50 1 - rewritable EC firmware
51
52
53 Below is a list of dictionaries to map current system state as reported by
54 'crossystem' into the 'legacy' boot vector digits.
55
56 The three elements of the list represent the three digits of the boot
57 vector. Each list element is a dictionary where the key is the legacy boot
58 vector value in the appropriate position, and the value is in turn a
59 dictionary of name-value pairs.
60
61 If all name-value pairs of a dictionary element match those reported by
62 crossystem, the legacy representation number is considered the appropriate
63 vector digit.
64
65 Note that on some platforms (namely, Mario) same parameters returned by
66 crossystem are set to a wrong value. The class init() routine adjust the
67 list to support those platforms.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +080068 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080069
70 VECTOR_MAPS = [
71 { # first vector position
72 '1': {
73 'devsw_boot': '0',
74 'mainfw_type': 'normal',
75 'recoverysw_boot': '0',
76 },
77 '2': {
78 'devsw_boot': '1',
79 'mainfw_type': 'developer',
80 'recoverysw_boot': '0',
81 },
82 '3': {
83 'devsw_boot': '0',
84 'mainfw_type': 'recovery',
85 'recovery_reason' : '2',
86 'recoverysw_boot': '1',
87 },
88 '4': {
89 'devsw_boot': '1',
90 'mainfw_type': 'recovery',
91 'recovery_reason' : '65',
92 'recoverysw_boot': '0',
93 },
94 '5': {
95 'devsw_boot': '0',
96 'mainfw_type': 'recovery',
97 'recovery_reason' : ('3', '23', '27'),
98 'recoverysw_boot': '0',
99 },
100 '6': {
101 'devsw_boot': '0',
102 'mainfw_type': 'recovery',
103 'recovery_reason' : '66',
104 'recoverysw_boot': '0',
105 },
106 '8': {
107 'devsw_boot': '0',
108 'mainfw_type': 'recovery',
109 'recovery_reason' : USER_RECOVERY_REQUEST_CODE,
110 'recoverysw_boot': '0',
111 },
112 },
113 { # second vector position
114 '0': {'mainfw_type': 'recovery',},
115 '1': {'mainfw_act': 'A',},
116 '2': {'mainfw_act': 'B',},
117 },
118 { # third vector position
119 '0': {'ecfw_act': 'RO',},
120 '1': {'ecfw_act': 'RW',},
121 },
122 ]
123
124 def init(self, cros_if):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800125 """Init the instance. If running on Mario - adjust the map."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800126
127 self.cros_if = cros_if
128
129 # Hack Alert!!! Adjust vector map to work on Mario
130 fwid = self.__getattr__('fwid').lower()
131 if not 'mario' in fwid:
132 return
133 # Mario firmware is broken and always reports recovery switch as set
134 # at boot time when booting up in recovery mode. This is why we
135 # exclude recoverysw_boot from the map when running on mario.
136 for state in self.VECTOR_MAPS[0].itervalues():
137 if state['mainfw_type'] != 'recovery':
138 continue
139 if 'recoverysw_boot' in state:
140 del(state['recoverysw_boot'])
141 if state['recovery_reason'] == self.USER_RECOVERY_REQUEST_CODE:
142 # This is the only recovery reason Mario knows about
143 state['recovery_reason'] = '1'
144
145 def __getattr__(self, name):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800146 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800147 Retrieve a crosssystem attribute.
148
149 Attempt to access crossystemobject.name will invoke `crossystem name'
150 and return the stdout as the value.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800151 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800152 return self.cros_if.run_shell_command_get_output(
153 'crossystem %s' % name)[0]
154
155 def __setattr__(self, name, value):
156 if name in ('cros_if',):
157 self.__dict__[name] = value
158 else:
159 self.cros_if.run_shell_command('crossystem "%s=%s"' % (name, value))
160
161 def request_recovery(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800162 """Request recovery mode next time the target reboots."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800163
164 self.__setattr__('recovery_request', self.USER_RECOVERY_REQUEST_CODE)
165
166 def get_boot_vector_base(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800167 """Convert system state into a legacy boot vector base.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800168
169 The function looks up the VECTOR_MAPS list above to find the digits
170 matching the current crossystem output, and returns a list of three
171 digits in symbolic representation, which become the base of the 5
172 digit boot state vector.
173
174 Should it be impossible to interpret the state, the function returns
175 a partially built list, which is an indication of a problem for the
176 caller (list shorter than 3 elements).
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800177 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800178
179 boot_vector = []
180
181 for vector_map in self.VECTOR_MAPS:
182 for (digit, values) in vector_map.iteritems():
183 for (name, value) in values.iteritems():
184 # Get the actual attribute value from crossystem.
185 attr_value = self.__getattr__(name)
186 if isinstance(value, str):
187 if attr_value != value:
188 break
189 else:
190 # 'value' is a tuple of possible actual values.
191 if attr_value not in value:
192 break
193 else:
194 boot_vector.append(digit)
195 break
196
197 return boot_vector
198
199 def dump(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800200 """Dump all crossystem values as multiline text."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800201
202 return '\n'.join(self.cros_if.run_shell_command_get_output(
203 'crossystem'))
204
205
206class ChromeOSInterface(object):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800207 """An object to encapsulate OS services functions."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800208
209 def __init__(self, silent):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800210 """Object construction time initialization.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800211
212 The only parameter is the Boolean 'silent', when True the instance
213 does not duplicate log messages on the console.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800214 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800215
216 self.silent = silent
217 self.state_dir = None
218 self.log_file = None
219 self.cs = Crossystem()
220
221 def init(self, state_dir=None, log_file=None):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800222 """Initialize the ChromeOS interface object.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800223 Args:
224 state_dir - a string, the name of the directory (as defined by the
225 caller). The contents of this directory persist over
226 system restarts and power cycles.
227 log_file - a string, the name of the log file kept in the state
228 directory.
229
230 Default argument values support unit testing.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800231 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800232
233 self.cs.init(self)
234 self.state_dir = state_dir
235
236 if self.state_dir:
237 if not os.path.exists(self.state_dir):
238 try:
239 os.mkdir(self.state_dir)
240 except OSError, err:
241 raise ChromeOSInterfaceError(err)
242 if log_file:
243 if log_file[0] == '/':
244 self.log_file = log_file
245 else:
246 self.log_file = os.path.join(state_dir, log_file)
247
248 def target_hosted(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800249 """Return True if running on a ChromeOS target."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800250 signature = open('/etc/lsb-release', 'r').readlines()[0]
251 return re.search(r'chrom(ium|e)os', signature, re.IGNORECASE) != None
252
253 def state_dir_file(self, file_name):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800254 """Get a full path of a file in the state directory."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800255 return os.path.join(self.state_dir, file_name)
256
257 def init_environment(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800258 """Initialize Chrome OS interface environment.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800259
260 If state dir was not set up by the constructor, create a temp
261 directory, otherwise create the directory defined during construction
262 of this object.
263
264 Return the state directory name.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800265 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800266
267 if not self.state_dir:
268 self.state_dir = tempfile.mkdtemp(suffix='_saft')
269 else:
270 # Wipe out state directory, to start the state machine clean.
271 shutil.rmtree(self.state_dir)
272 # And recreate it
273 self.init(self.state_dir, self.log_file)
274
275 return self.state_dir
276
277 def shut_down(self, new_log='/var/saft_log.txt'):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800278 """Destroy temporary environment so that the test can be restarted."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800279 if os.path.exists(self.log_file):
280 shutil.copyfile(self.log_file, new_log)
281 shutil.rmtree(self.state_dir)
282
283 def log(self, text):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800284 """Write text to the log file and print it on the screen, if enabled.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800285
286 The entire log (maintained across reboots) can be found in
287 self.log_file.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800288 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800289
290 # Don't print on the screen unless enabled.
291 if not self.silent:
292 print text
293
294 if not self.log_file or not os.path.exists(self.state_dir):
295 # Called before environment was initialized, ignore.
296 return
297
298 timestamp = datetime.datetime.strftime(
299 datetime.datetime.now(), '%I:%M:%S %p:')
300
301 log_f = open(self.log_file, 'a')
302 log_f.write('%s %s\n' % (timestamp, text))
303 log_f.close()
304
305 def exec_exists(self, program):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800306 """Check if the passed in string is a valid executable found in PATH."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800307
308 for path in os.environ['PATH'].split(os.pathsep):
309 exe_file = os.path.join(path, program)
310 if (os.path.isfile(exe_file) or os.path.islink(exe_file)
311 ) and os.access(exe_file, os.X_OK):
312 return True
313 return False
314
315 def run_shell_command(self, cmd):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800316 """Run a shell command.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800317
318 In case of the command returning an error print its stdout and stderr
319 outputs on the console and dump them into the log. Otherwise suppress all
320 output.
321
322 In case of command error raise an OSInterfaceError exception.
323
324 Return the subprocess.Popen() instance to provide access to console
325 output in case command succeeded.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800326 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800327
328 self.log('Executing %s' % cmd)
329 process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
330 stderr=subprocess.PIPE)
331 process.wait()
332 if process.returncode:
333 err = ['Failed running: %s' % cmd]
334 err.append('stdout:')
335 err.append(process.stdout.read())
336 err.append('stderr:')
337 err.append(process.stderr.read())
338 text = '\n'.join(err)
339 print text
340 self.log(text)
341 raise ChromeOSInterfaceError('command %s failed' % cmd)
342 return process
343
344 def is_removable_device(self, device):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800345 """Check if a certain storage device is removable.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800346
347 device - a string, file name of a storage device or a device partition
348 (as in /dev/sda[0-9] or /dev/mmcblk0p[0-9]).
349
350 Returns True if the device is removable, False if not.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800351 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800352
353 if not self.target_hosted():
354 return False
355
356 # Drop trailing digit(s) and letter(s) (if any)
357 base_dev = self.strip_part(device.split('/')[2])
358 removable = int(open('/sys/block/%s/removable' % base_dev, 'r').read())
359
360 return removable == 1
361
362 def get_internal_disk(self, device):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800363 """Get the internal disk by given the current disk.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800364
365 If device is removable device, internal disk is decided by which kind
366 of divice (arm or x86). Otherwise, return device itself.
367
368 device - a string, file name of a storage device or a device partition
369 (as in /dev/sda[0-9] or /dev/mmcblk0p[0-9]).
370
371 Return internal kernel disk.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800372 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800373 if self.is_removable_device(device):
Tom Wai-Hong Tambc750942012-09-14 11:49:17 +0800374 if os.path.exists('/dev/mmcblk0'):
375 return '/dev/mmcblk0'
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800376 else:
377 return '/dev/sda'
378 else:
379 return self.strip_part(device)
380
381 def get_root_part(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800382 """Return a string, the name of root device with partition number"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800383 return self.run_shell_command_get_output('rootdev -s')[0]
384
385 def get_root_dev(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800386 """Return a string, the name of root device without partition number"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800387 return self.strip_part(self.get_root_part())
388
389 def join_part(self, dev, part):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800390 """Return a concatenated string of device and partition number"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800391 if 'mmcblk' in dev:
392 return dev + 'p' + part
393 else:
394 return dev + part
395
396 def strip_part(self, dev_with_part):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800397 """Return a stripped string without partition number"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800398 dev_name_stripper = re.compile('p?[0-9]+$')
399 return dev_name_stripper.sub('', dev_with_part)
400
401 def run_shell_command_get_output(self, cmd):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800402 """Run shell command and return its console output to the caller.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800403
404 The output is returned as a list of strings stripped of the newline
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800405 characters."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800406
407 process = self.run_shell_command(cmd)
408 return [x.rstrip() for x in process.stdout.readlines()]
409
410 def boot_state_vector(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800411 """Read and return to caller a string describing the system state.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800412
413 The string has a form of x0:x1:x2:<removable>:<partition_number>,
414 where the field meanings of X# are described in the
415 Crossystem.get_boot_vector_base() docstring above.
416
417 <removable> is set to 1 or 0 depending if the root device is removable
418 or not, and <partition number> is the last element of the root device
419 name, designating the partition where the root fs is mounted.
420
421 This vector fully describes the way the system came up.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800422 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800423
424 state = self.cs.get_boot_vector_base()
425
426 if len(state) != 3:
427 raise ChromeOSInterfaceError(self.cs.dump())
428
429 root_part = self.get_root_part()
430 state.append('%d' % int(self.is_removable_device(root_part)))
431 state.append('%s' % root_part[-1])
432 state_str = ':'.join(state)
433 return state_str
434
435 def cmp_boot_vector(self, vector1, vector2):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800436 """Compare if the two boot vectors are the same
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800437
438 Note: a wildcard (*) will match any value.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800439 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800440 list1 = vector1.split(':')
441 list2 = vector2.split(':')
442 if len(list1) != len(list2):
443 raise ChromeOSInterfaceError(
444 'Boot vectors (%s %s) should be of the same length'
445 % (vecotr1, vector2))
446 for i in range(len(list1)):
447 if list1[i] != list2[i] and list1[i] != '*' and list2[i] != '*':
448 return False
449 return True
450
451 def get_writeable_mount_point(self, dev, tmp_dir):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800452 """Get mountpoint of the passed in device mounted in read/write mode.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800453
454 If the device is already mounted and is writeable - return its mount
455 point. If the device is mounted but read-only - remount it read/write
456 and return its mount point. If the device is not mounted - mount it read
457 write on the passsed in path and return this path.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800458 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800459
460 # The device root file system is mounted on is represented as /dev/root
461 # otherwise.
462 options_filter = re.compile('.*\((.+)\).*')
463 root_part = self.get_root_part()
464 if dev == root_part:
465 dev = '/dev/root'
466
467 for line in self.run_shell_command_get_output('mount'):
468 if not line.startswith('%s ' % dev):
469 continue
470 mount_options = options_filter.match(line).groups(0)[0]
471 # found mounted
472 if 'ro' in mount_options.split(','):
473 # mounted read only
474 self.run_shell_command('mount -o remount,rw %s' % dev)
475 return line.split()[2] # Mountpoint is the third element.
476 # Not found, needs to be mounted
477 self.run_shell_command('mount %s %s' % (dev, tmp_dir))
478 return tmp_dir
479
480 def retrieve_body_version(self, blob):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800481 """Given a blob, retrieve body version.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800482
483 Currently works for both, firmware and kernel blobs. Returns '-1' in
484 case the version can not be retrieved reliably.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800485 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800486 header_format = '<8s8sQ'
487 preamble_format = '<40sQ'
488 magic, _, kb_size = struct.unpack_from(header_format, blob)
489
490 if magic != 'CHROMEOS':
491 return -1 # This could be a corrupted version case.
492
493 _, version = struct.unpack_from(preamble_format, blob, kb_size)
494 return version
495
496 def retrieve_datakey_version(self, blob):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800497 """Given a blob, retrieve firmware data key version.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800498
499 Currently works for both, firmware and kernel blobs. Returns '-1' in
500 case the version can not be retrieved reliably.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800501 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800502 header_format = '<8s96sQ'
503 magic, _, version = struct.unpack_from(header_format, blob)
504 if magic != 'CHROMEOS':
505 return -1 # This could be a corrupted version case.
506 return version
507
508 def retrieve_kernel_subkey_version(self, blob):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800509 """Given a blob, retrieve kernel subkey version.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800510
511 It is in firmware vblock's preamble.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800512 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800513
514 header_format = '<8s8sQ'
515 preamble_format = '<72sQ'
516 magic, _, kb_size = struct.unpack_from(header_format, blob)
517
518 if magic != 'CHROMEOS':
519 return -1
520
521 _, version = struct.unpack_from(preamble_format, blob, kb_size)
522 return version
523
524 def retrieve_preamble_flags(self, blob):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800525 """Given a blob, retrieve preamble flags if available.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800526
527 It only works for firmware. If the version of preamble header is less
528 than 2.1, no preamble flags supported, just returns 0.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800529 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800530 header_format = '<8s8sQ'
531 preamble_format = '<32sII64sI'
532 magic, _, kb_size = struct.unpack_from(header_format, blob)
533
534 if magic != 'CHROMEOS':
535 return -1 # This could be a corrupted version case.
536
537 _, ver, subver, _, flags = struct.unpack_from(preamble_format, blob,
538 kb_size)
539
540 if ver > 2 or (ver == 2 and subver >= 1):
541 return flags
542 else:
543 return 0 # Returns 0 if preamble flags not available.
544
545 def read_partition(self, partition, size):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800546 """Read the requested partition, up to size bytes."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800547 tmp_file = self.state_dir_file('part.tmp')
548 self.run_shell_command('dd if=%s of=%s bs=1 count=%d' % (
549 partition, tmp_file, size))
550 fileh = open(tmp_file, 'r')
551 data = fileh.read()
552 fileh.close()
553 os.remove(tmp_file)
554 return data