blob: 4902a1efe1c45aebec2eb015decff6c3c361c235 [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
Tom Wai-Hong Tam48ae2212013-06-26 10:37:40 +0800301 with open(self.log_file, 'a') as log_f:
302 log_f.write('%s %s\n' % (timestamp, text))
303 log_f.flush()
304 os.fdatasync(log_f)
305
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800306
307 def exec_exists(self, program):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800308 """Check if the passed in string is a valid executable found in PATH."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800309
310 for path in os.environ['PATH'].split(os.pathsep):
311 exe_file = os.path.join(path, program)
312 if (os.path.isfile(exe_file) or os.path.islink(exe_file)
313 ) and os.access(exe_file, os.X_OK):
314 return True
315 return False
316
317 def run_shell_command(self, cmd):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800318 """Run a shell command.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800319
320 In case of the command returning an error print its stdout and stderr
321 outputs on the console and dump them into the log. Otherwise suppress all
322 output.
323
324 In case of command error raise an OSInterfaceError exception.
325
326 Return the subprocess.Popen() instance to provide access to console
327 output in case command succeeded.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800328 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800329
330 self.log('Executing %s' % cmd)
331 process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
332 stderr=subprocess.PIPE)
333 process.wait()
334 if process.returncode:
335 err = ['Failed running: %s' % cmd]
336 err.append('stdout:')
337 err.append(process.stdout.read())
338 err.append('stderr:')
339 err.append(process.stderr.read())
340 text = '\n'.join(err)
341 print text
342 self.log(text)
343 raise ChromeOSInterfaceError('command %s failed' % cmd)
344 return process
345
346 def is_removable_device(self, device):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800347 """Check if a certain storage device is removable.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800348
349 device - a string, file name of a storage device or a device partition
350 (as in /dev/sda[0-9] or /dev/mmcblk0p[0-9]).
351
352 Returns True if the device is removable, False if not.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800353 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800354
355 if not self.target_hosted():
356 return False
357
358 # Drop trailing digit(s) and letter(s) (if any)
359 base_dev = self.strip_part(device.split('/')[2])
360 removable = int(open('/sys/block/%s/removable' % base_dev, 'r').read())
361
362 return removable == 1
363
364 def get_internal_disk(self, device):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800365 """Get the internal disk by given the current disk.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800366
367 If device is removable device, internal disk is decided by which kind
368 of divice (arm or x86). Otherwise, return device itself.
369
370 device - a string, file name of a storage device or a device partition
371 (as in /dev/sda[0-9] or /dev/mmcblk0p[0-9]).
372
373 Return internal kernel disk.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800374 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800375 if self.is_removable_device(device):
Tom Wai-Hong Tambc750942012-09-14 11:49:17 +0800376 if os.path.exists('/dev/mmcblk0'):
377 return '/dev/mmcblk0'
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800378 else:
379 return '/dev/sda'
380 else:
381 return self.strip_part(device)
382
383 def get_root_part(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800384 """Return a string, the name of root device with partition number"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800385 return self.run_shell_command_get_output('rootdev -s')[0]
386
387 def get_root_dev(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800388 """Return a string, the name of root device without partition number"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800389 return self.strip_part(self.get_root_part())
390
391 def join_part(self, dev, part):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800392 """Return a concatenated string of device and partition number"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800393 if 'mmcblk' in dev:
394 return dev + 'p' + part
395 else:
396 return dev + part
397
398 def strip_part(self, dev_with_part):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800399 """Return a stripped string without partition number"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800400 dev_name_stripper = re.compile('p?[0-9]+$')
401 return dev_name_stripper.sub('', dev_with_part)
402
403 def run_shell_command_get_output(self, cmd):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800404 """Run shell command and return its console output to the caller.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800405
406 The output is returned as a list of strings stripped of the newline
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800407 characters."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800408
409 process = self.run_shell_command(cmd)
410 return [x.rstrip() for x in process.stdout.readlines()]
411
412 def boot_state_vector(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800413 """Read and return to caller a string describing the system state.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800414
415 The string has a form of x0:x1:x2:<removable>:<partition_number>,
416 where the field meanings of X# are described in the
417 Crossystem.get_boot_vector_base() docstring above.
418
419 <removable> is set to 1 or 0 depending if the root device is removable
420 or not, and <partition number> is the last element of the root device
421 name, designating the partition where the root fs is mounted.
422
423 This vector fully describes the way the system came up.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800424 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800425
426 state = self.cs.get_boot_vector_base()
427
428 if len(state) != 3:
429 raise ChromeOSInterfaceError(self.cs.dump())
430
431 root_part = self.get_root_part()
432 state.append('%d' % int(self.is_removable_device(root_part)))
433 state.append('%s' % root_part[-1])
434 state_str = ':'.join(state)
435 return state_str
436
437 def cmp_boot_vector(self, vector1, vector2):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800438 """Compare if the two boot vectors are the same
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800439
440 Note: a wildcard (*) will match any value.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800441 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800442 list1 = vector1.split(':')
443 list2 = vector2.split(':')
444 if len(list1) != len(list2):
445 raise ChromeOSInterfaceError(
446 'Boot vectors (%s %s) should be of the same length'
447 % (vecotr1, vector2))
448 for i in range(len(list1)):
449 if list1[i] != list2[i] and list1[i] != '*' and list2[i] != '*':
450 return False
451 return True
452
453 def get_writeable_mount_point(self, dev, tmp_dir):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800454 """Get mountpoint of the passed in device mounted in read/write mode.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800455
456 If the device is already mounted and is writeable - return its mount
457 point. If the device is mounted but read-only - remount it read/write
458 and return its mount point. If the device is not mounted - mount it read
459 write on the passsed in path and return this path.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800460 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800461
462 # The device root file system is mounted on is represented as /dev/root
463 # otherwise.
464 options_filter = re.compile('.*\((.+)\).*')
465 root_part = self.get_root_part()
466 if dev == root_part:
467 dev = '/dev/root'
468
469 for line in self.run_shell_command_get_output('mount'):
470 if not line.startswith('%s ' % dev):
471 continue
472 mount_options = options_filter.match(line).groups(0)[0]
473 # found mounted
474 if 'ro' in mount_options.split(','):
475 # mounted read only
476 self.run_shell_command('mount -o remount,rw %s' % dev)
477 return line.split()[2] # Mountpoint is the third element.
478 # Not found, needs to be mounted
479 self.run_shell_command('mount %s %s' % (dev, tmp_dir))
480 return tmp_dir
481
482 def retrieve_body_version(self, blob):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800483 """Given a blob, retrieve body version.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800484
485 Currently works for both, firmware and kernel blobs. Returns '-1' in
486 case the version can not be retrieved reliably.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800487 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800488 header_format = '<8s8sQ'
489 preamble_format = '<40sQ'
490 magic, _, kb_size = struct.unpack_from(header_format, blob)
491
492 if magic != 'CHROMEOS':
493 return -1 # This could be a corrupted version case.
494
495 _, version = struct.unpack_from(preamble_format, blob, kb_size)
496 return version
497
498 def retrieve_datakey_version(self, blob):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800499 """Given a blob, retrieve firmware data key version.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800500
501 Currently works for both, firmware and kernel blobs. Returns '-1' in
502 case the version can not be retrieved reliably.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800503 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800504 header_format = '<8s96sQ'
505 magic, _, version = struct.unpack_from(header_format, blob)
506 if magic != 'CHROMEOS':
507 return -1 # This could be a corrupted version case.
508 return version
509
510 def retrieve_kernel_subkey_version(self, blob):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800511 """Given a blob, retrieve kernel subkey version.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800512
513 It is in firmware vblock's preamble.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800514 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800515
516 header_format = '<8s8sQ'
517 preamble_format = '<72sQ'
518 magic, _, kb_size = struct.unpack_from(header_format, blob)
519
520 if magic != 'CHROMEOS':
521 return -1
522
523 _, version = struct.unpack_from(preamble_format, blob, kb_size)
524 return version
525
526 def retrieve_preamble_flags(self, blob):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800527 """Given a blob, retrieve preamble flags if available.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800528
529 It only works for firmware. If the version of preamble header is less
530 than 2.1, no preamble flags supported, just returns 0.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800531 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800532 header_format = '<8s8sQ'
533 preamble_format = '<32sII64sI'
534 magic, _, kb_size = struct.unpack_from(header_format, blob)
535
536 if magic != 'CHROMEOS':
537 return -1 # This could be a corrupted version case.
538
539 _, ver, subver, _, flags = struct.unpack_from(preamble_format, blob,
540 kb_size)
541
542 if ver > 2 or (ver == 2 and subver >= 1):
543 return flags
544 else:
545 return 0 # Returns 0 if preamble flags not available.
546
547 def read_partition(self, partition, size):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800548 """Read the requested partition, up to size bytes."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800549 tmp_file = self.state_dir_file('part.tmp')
550 self.run_shell_command('dd if=%s of=%s bs=1 count=%d' % (
551 partition, tmp_file, size))
552 fileh = open(tmp_file, 'r')
553 data = fileh.read()
554 fileh.close()
555 os.remove(tmp_file)
556 return data