gooftool: merge ToT changes into R12 factory, to support latest HWID design
gooftool/write_gbb: never change keys in GBB
Cherry-picked: http://gerrit.chromium.org/gerrit/2030
factory_test_tools: introducing new firmware hash algorithm
Cherry-picked: http://gerrit.chromium.org/gerrit/2858
factory_test_tools: add 'hash.db' for hash value virtualization
Cherry-picked: http://gerrit.chromium.org/gerrit/3236
factory_test_tools: fix recoverykey / gbb retrieval error
Cherry-picked: http://gerrit.chromium.org/gerrit/3237
factory_test_tools: support new chromeos-hwid default location (/usr/*/share/chromeos-hwid)
Cherry-picked: http://gerrit.chromium.org/gerrit/2866
factory_test_tools: fix component list relative path
Cherry-picked: http://gerrit.chromium.org/gerrit/3374
factory_test_tools: fix vblock test code
Cherry-picked: http://gerrit.chromium.org/gerrit/3995
gooftool: add bcdDevice and storage size into component list
Cherry-picked: http://gerrit.chromium.org/gerrit/4085
gooftool: improve match results failure
Cherry-picked: http://gerrit.chromium.org/gerrit/4994
gft_hwcomp: separate vendor_id_touchpad into id and firmware
Cherry-picked: http://gerrit.chromium.org/gerrit/5294
gft_hwcomp: support finding ARM memory size by kernel command line
Cherry-picked: http://gerrit.chromium.org/gerrit/5372
gft_hwcomp: improve camera detection by using v4l2 interface
Cherry-picked: http://gerrit.chromium.org/gerrit/5387
gft_hwcomp: refine property names
Cherry-picked: http://gerrit.chromium.org/gerrit/5388
gft_hwcomp: provide chipset name for SOC systems
Cherry-picked: http://gerrit.chromium.org/gerrit/5439
factory_test_tools: make sure partitions are in good priority before wipe
Cherry-picked: http://gerrit.chromium.org/gerrit/499
BUG=none, prepare for next factory bundle delivery
TEST=factory will verify
Change-Id: Ic5a06be55ed44bceeecd3e9da2a2c660c2520b64
Reviewed-on: http://gerrit.chromium.org/gerrit/5647
Reviewed-by: Jay Kim <yongjaek@chromium.org>
Tested-by: Hung-Te Lin <hungte@chromium.org>
diff --git a/edid.py b/edid.py
new file mode 100755
index 0000000..235f775
--- /dev/null
+++ b/edid.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""EDID Parser (light-weight parse-edid replacement)
+
+This module provides simple parsing of EDID (
+http://en.wikipedia.org/wiki/Extended_display_identification_data ) because the
+full-feature parser (parse-edid) has too much dependency.
+
+TODO(hungte) Use parse-edid if available.
+"""
+
+
+import sys
+
+
+# EDID Constants
+EDID_VERSION = 0x01
+EDID_MAGIC = '\x00\xff\xff\xff\xff\xff\xff\x00'
+
+EDID_MAGIC_OFFSET = 0
+EDID_MANUFACTURER_ID_OFFSET = 8
+EDID_PRODUCT_ID_OFFSET = 10
+EDID_VERSION_OFFSET = 18
+EDID_REVISION_OFFSET = 19
+EDID_PIXEL_CLOCK_OFFSET = 54
+EDID_HORIZONTAL_OFFSET = 56
+EDID_HORIZONTAL_HIGH_OFFSET = 58
+EDID_VERTICAL_OFFSET = 59
+EDID_VERTICAL_HIGH_OFFSET = 61
+EDID_CHECKSUM_OFFSET = 127
+EDID_MINIMAL_SIZE = 128
+EDID_MANUFACTURER_ID_BITS = 5
+
+EDID_MANUFACTURER_ID = 'manufactuer_id'
+EDID_PRODUCT_ID = 'product_id'
+EDID_WIDTH = 'width'
+EDID_HEIGHT = 'height'
+
+
+def parse_edid(blob):
+ """Parses a in-memory EDID blob.
+
+ Args:
+ blob: a binary blob with encoded EDID.
+
+ Returns:
+ A dictionary of extracted information, or raise ValueError if the blob is
+ not a valid EDID record.
+ """
+
+ def read_short(offset):
+ return ((ord(blob[offset]) << 8) | ord(blob[offset + 1]))
+
+ data = {}
+
+ # Check size, magic, and version
+ if len(blob) < EDID_MINIMAL_SIZE:
+ raise ValueError("Length too small")
+ if (blob[EDID_MAGIC_OFFSET:(EDID_MAGIC_OFFSET + len(EDID_MAGIC))] !=
+ EDID_MAGIC):
+ raise ValueError("Incorrect header")
+ if ord(blob[EDID_VERSION_OFFSET]) != EDID_VERSION:
+ raise ValueError("Unsupported EDID version")
+
+ # Verify checksum
+ if sum([ord(char) for char in blob[:EDID_CHECKSUM_OFFSET+1]]) % 0x100 != 0:
+ raise ValueError("Checksum error.")
+
+ # Currently we don't support EDID not using pixel clock
+ pixel_clock = read_short(EDID_PIXEL_CLOCK_OFFSET)
+ if not pixel_clock:
+ raise ValueError("Non-pixel clock format is not supported yet")
+
+ # Extract manufactuer
+ vendor_name = ''
+ vendor_code = read_short(EDID_MANUFACTURER_ID_OFFSET)
+
+ # vendor_code: [0 | char1 | char2 | char3]
+ for i in range(2, -1, -1):
+ vendor_char = (vendor_code >> (i * EDID_MANUFACTURER_ID_BITS)) & 0x1F
+ vendor_char = chr(vendor_char + ord('@'))
+ vendor_name += vendor_char
+ data[EDID_MANUFACTURER_ID] = vendor_name
+
+ product_id = read_short(EDID_PRODUCT_ID_OFFSET)
+ data[EDID_PRODUCT_ID] = product_id
+
+ width = (ord(blob[EDID_HORIZONTAL_OFFSET]) |
+ ((ord(blob[EDID_HORIZONTAL_HIGH_OFFSET]) >> 4) << 8))
+ height = (ord(blob[EDID_VERTICAL_OFFSET]) |
+ ((ord(blob[EDID_VERTICAL_HIGH_OFFSET]) >> 4) << 8))
+ data[EDID_WIDTH] = width
+ data[EDID_HEIGHT] = height
+ return data
+
+
+def parse_edid_file(file_name):
+ with open(file_name, 'r') as edid_handle:
+ return parse_edid(edid_handle.read(EDID_MINIMAL_SIZE))
+
+
+if __name__ == '__main__':
+ if len(sys.argv) < 2:
+ print parse_edid(sys.stdin.read())
+ else:
+ for edid_file in sys.argv[1:]:
+ print parse_edid_file(edid_file)
diff --git a/flashrom_util.py b/flashrom_util.py
index 9326102..6ec418c 100644
--- a/flashrom_util.py
+++ b/flashrom_util.py
@@ -993,6 +993,46 @@
# ---------------------------------------------------------------------------
+# Simple access to a FMAP based firmware images
+class FirmwareImage(object):
+ """Provides access to firmware image via FMAP sections."""
+ def __init__(self, image_source):
+ self._image = image_source;
+ self._fmap = fmap.fmap_decode(self._image)
+ self._areas = dict(
+ (entry['name'], [entry['offset'], entry['size']])
+ for entry in self._fmap['areas'])
+
+ def has_section(self, section_name):
+ """Returns if specified section is available in image."""
+ return section_name in self._areas
+
+ def get_section(self, section_name):
+ """Returns the content of specified section."""
+ if not self.has_section(section_name):
+ raise Exception('get_section: invalid section "%s".' % section_name)
+ area = self._areas[section_name]
+ return self._image[area[0]:(area[0] + area[1])]
+
+ def put_section(self, section_name, value):
+ """Updates content of specified section in image."""
+ if not self.has_section(section_name):
+ raise Exception("Section does not exist: %s" % section_name)
+ area = self._areas[section_name]
+ if len(value) != area[1]:
+ raise ValueError("Value size (%d) does not fit into section (%s, %d)" %
+ (len(value), section_name, area[1]))
+ self._image = (self._image[0:area[0]] +
+ value +
+ self._image[(area[0] + area[1]):])
+ return True
+
+ def get_fmap_blob(self):
+ """Returns the re-encoded fmap blob from firmware image."""
+ return fmap.fmap_encode(self._fmap)
+
+
+# ---------------------------------------------------------------------------
# The flashrom_util supports both running inside and outside 'autotest'
# framework, so we need to provide some mocks and dynamically load
# autotest components here.
diff --git a/gft_common.py b/gft_common.py
index 54ff6a4..2f1fdbb 100644
--- a/gft_common.py
+++ b/gft_common.py
@@ -226,6 +226,11 @@
with open(filename, "w") as opened_file:
opened_file.write(data)
+def WriteBinaryFile(filename, data):
+ """ Writes one binary file and exit. """
+ with open(filename, "wb") as opened_file:
+ opened_file.write(data)
+
def GetSystemArch():
"""Gets current system architecture, in portage-style return value.
@@ -312,6 +317,23 @@
return data
+def ExpandComponentsDatabaseHash(data, database_file):
+ """ Processes and expands virtual hash in a component database. """
+ hashdb_file = os.path.join(os.path.dirname(database_file), 'hash.db')
+ if not os.path.exists(hashdb_file):
+ return data
+ hashdb = eval(ReadFile(hashdb_file))
+ for (key, values) in data.items():
+ if not key.startswith('hash'):
+ continue
+ new_values = [hashdb.get(value, value) for value in values]
+ if new_values != values:
+ data[key] = new_values
+ DebugMsg('ExpandComponentsDatabaseHash: %s: %s -> %s' %
+ (key, values, new_values))
+ return data
+
+
def LoadComponentsDatabaseFile(filename):
""" Loads a components database file. """
original_filename = filename
@@ -339,14 +361,15 @@
filename = os.path.join(base_dir, new_file)
if len(file_list) > 1:
DebugMsg('LoadComponentsDatabaseFile: ' + '->'.join(file_list))
+ ExpandComponentsDatabaseHash(result, original_filename)
ExpandComponentsDatabaseMacro(result, original_filename)
return result
def GetComponentsDatabaseBase(database_file):
""" Gets the base folder of a components database file. """
- # Currently the database file is assuming properties sit in its parent folder.
- base = os.path.join(os.path.split(os.path.abspath(database_file))[0], '..')
+ # Currently the database file is assuming properties sit in same folder.
+ base = os.path.dirname(os.path.abspath(database_file))
return os.path.abspath(base)
diff --git a/gft_fwhash.py b/gft_fwhash.py
index 44ac17b..ecb30ac 100755
--- a/gft_fwhash.py
+++ b/gft_fwhash.py
@@ -15,114 +15,270 @@
from gft_common import WarningMsg, VerboseMsg, DebugMsg, ErrorMsg, ErrorDie
-def GetBIOSReadOnlyHash(file_source=None):
+# Global Variable
+_HashAlgorithms = {}
+
+
+# Utility Function
+def HashAlgorithm(func):
+ """Decorator for algorithm definition. """
+ def _wrapped(*arg, **kargs):
+ result = func(*arg, **kargs)
+ (target, version) = _wrapped.__name__.split('_')
+ code = target.lower()[0]
+ return '%c%s#%s' % (code, version, result)
+ name = func.__name__
+ assert name not in _HashAlgorithms
+ _wrapped.__name__ = name
+ _HashAlgorithms[name] = _wrapped
+ return _wrapped
+
+
+def DefaultHashAlgorithm(func):
+ """Decorator of default algorithm. """
+ name = func.__name__
+ (target, code) = name.split('_')
+ new_func = HashAlgorithm(func)
+ assert target not in _HashAlgorithms
+ _HashAlgorithms[target] = new_func
+ return new_func
+
+
+def FindAlgorithm(target, version):
+ """Returns a registered algorithm"""
+ if version:
+ name = '%s_%s' % (target, version)
+ else:
+ name = target
+ if name not in _HashAlgorithms:
+ ErrorDie('Unknown algorithm: %s' % name)
+ return _HashAlgorithms[name]
+
+
+def GetGbbKey(image_file, name):
+ """
+ Reads GBB key property from a firmware image.
+
+ Args:
+ image_file: the file name of main firmware image.
+ name: name of the key to retrieve (see gbb_utility).
+ """
+ filename = gft_common.GetTemporaryFileName('gbb%s' % name)
+ try:
+ gft_common.System('gbb_utility -g --%s=%s %s' %
+ (name, filename, image_file))
+ return gft_common.ReadBinaryFile(filename)
+ finally:
+ os.remove(filename)
+
+
+# Hash Algorithms
+@HashAlgorithm
+def Main_v1(image_source):
+ """ Algorithm: sha256(fmap, RO_SECTION) """
+ image = flashrom_util.FirmwareImage(image_source)
+ hash_src = image.get_fmap_blob()
+ if image.has_section('RO_SECTION'):
+ hash_src += image.get_section('RO_SECTION')
+ else:
+ # legacy section
+ hash_src += image.get_section('BOOT_STUB')
+ hash_src += image.get_section('GBB')
+ hash_src += image.get_section('RECOVERY')
+ return hashlib.sha256(hash_src).hexdigest()
+
+
+@DefaultHashAlgorithm
+def Main_v2(image_source):
+ """ Algorithm: sha256(fmap, RO_SECTION[-GBB]) """
+ image = flashrom_util.FirmwareImage(image_source)
+ hash_src = image.get_fmap_blob()
+ gbb = image.get_section('GBB')
+ zero_gbb = chr(0) * len(gbb)
+ image.put_section('GBB', zero_gbb)
+ hash_src += image.get_section('RO_SECTION')
+ return hashlib.sha256(hash_src).hexdigest()
+
+
+@HashAlgorithm
+def Ec_v1(image_source):
+ """ Algorithm: sha256(whole_image) """
+ hash_src = image_source
+ return hashlib.sha256(hash_src).hexdigest()
+
+
+@DefaultHashAlgorithm
+def Ec_v2(image_source):
+ """ Algorithm: sha256(fmap, EC_RO) """
+ image = flashrom_util.FirmwareImage(image_source)
+ hash_src = image.get_fmap_blob()
+ hash_src += image.get_section('EC_RO')
+ return hashlib.sha256(hash_src).hexdigest()
+
+
+@HashAlgorithm
+def Gbb_v1(image_source):
+ """ Algorithm: sha256(GBB) """
+ image = flashrom_util.FirmwareImage(image_source)
+ hash_src = image.get_section('GBB')
+ return hashlib.sha256(hash_src).hexdigest()
+
+
+@DefaultHashAlgorithm
+def Gbb_v2(image_source):
+ """ Algorithm: sha256(GBB-HWID) """
+ image = flashrom_util.FirmwareImage(image_source)
+ temp_file = gft_common.GetTemporaryFileName()
+ try:
+ # HWID can be checked explicitly, so we temporarily overwrite the HWID with
+ # a fixed string for computing the hash, so the hash doesn't depend on the
+ # final HWID.
+ reference_hwid = 'ChromeOS'
+ gft_common.WriteBinaryFile(temp_file, image.get_section('GBB'))
+ gft_common.System('gbb_utility -s --hwid="%s" "%s"' %
+ (reference_hwid, temp_file))
+ hash_src = gft_common.ReadBinaryFile(temp_file)
+ finally:
+ os.remove(temp_file)
+ return hashlib.sha256(hash_src).hexdigest()
+
+
+@HashAlgorithm
+def Key_v1(key_blob):
+ """ Algorithm: sha256(key) """
+ hash_src = key_blob
+ return hashlib.sha256(hash_src).hexdigest()
+
+
+@DefaultHashAlgorithm
+def Key_v2(key_blob):
+ """ Algorithm: sha256(key%%[00|FF])) """
+ # keys may be padded with 0x00 or 0xFF.
+ hash_src = key_blob
+ hash_src = hash_src.strip(chr(0)).strip(chr(0xFF))
+ return hashlib.sha256(hash_src).hexdigest()
+
+
+# Public API
+def GetMainFirmwareReadOnlyHash(file_source=None, algorithm=None):
"""
Returns a hash of main firmware (BIOS) read only parts,
to confirm we have proper keys / boot code / recovery image installed.
Args:
- file_source: None to read BIOS from system flash rom, or any string
- value as the file name of firmware image to read.
+ file_source: None to read firmware from system flash chip, otherwise
+ a file name of firmware image to read.
+ algorithm: The algorithm to generate hash value
"""
- # hash_ro_list: RO section to be hashed
- hash_src = ''
- hash_ro_list = ['RO_SECTION']
-
flashrom = flashrom_util.FlashromUtility(exception_type=gft_common.GFTError)
flashrom.initialize(flashrom.TARGET_BIOS, target_file=file_source)
-
image = flashrom.get_current_image()
- fmap_obj = fmap.fmap_decode(image)
- if not fmap_obj:
- ErrorDie('GetBIOSReadOnlyHash: No FMAP structure in flashrom.')
-
- # TODO(hungte) we can check that FMAP must reside in RO section, and the
- # BSTUB must be aligned to bottom of firmware.
- hash_src = hash_src + fmap.fmap_encode(fmap_obj)
-
- # New firmware spec defined new "RO_SECTION" which includes all sections to
- # be hashed. Legacy firmware uses a list of BSTUB, GBB, and DEV.
- if hash_ro_list[0] not in flashrom.layout:
- WarningMsg("Warning: firmware_hash: working on legacy firmware")
- hash_ro_list = ['BOOT_STUB', 'GBB', 'RECOVERY']
-
- for section in hash_ro_list:
- src = flashrom.read_section(section)
- if not src:
- ErrorDie('GetBIOSReadOnlyHash: Cannot get section [%s]' % section)
- hash_src = hash_src + src
- if not hash_src:
- ErrorDie('GetBIOSReadOnlyHash: Invalid hash source from flashrom.')
-
- return hashlib.sha256(hash_src).hexdigest()
+ algorithm = FindAlgorithm('Main', algorithm)
+ return algorithm(image)
-def GetECHash(file_source=None):
+def GetMainFirmwareGbbHash(file_source=None, algorithm=None):
"""
- Returns a hash of Embedded Controller firmware parts,
+ Returns a hash of main firmware (BIOS) GBB section,
+ to confirm we have proper keys / images / HWID.
+
+ Args:
+ file_source: None to read firmware from system flash chip, otherwise
+ a file name of firmware image to read.
+ algorithm: The algorithm to generate hash value
+ """
+ flashrom = flashrom_util.FlashromUtility(exception_type=gft_common.GFTError)
+ flashrom.initialize(flashrom.TARGET_BIOS, target_file=file_source)
+ image = flashrom.get_current_image()
+ algorithm = FindAlgorithm('Gbb', algorithm)
+ return algorithm(image)
+
+
+def GetEcFirmwareReadOnlyHash(file_source=None, algorithm=None):
+ """
+ Returns a hash of Embedded Controller firmware read only parts,
to confirm we have proper updated version of EC firmware.
Args:
- file_source: None to read BIOS from system flash rom, or any string
- value as the file name of firmware image to read.
+ file_source: None to read firmware from system flash chip, otherwise
+ a file name of firmware image to read.
+ algorithm: The algorithm to generate hash value
"""
flashrom = flashrom_util.FlashromUtility(exception_type=gft_common.GFTError)
flashrom.initialize(flashrom.TARGET_EC, target_file=file_source)
- # to bypass the 'skip verification' sections
image = flashrom.get_current_image()
- if not image:
- ErrorDie('GetECHash: Cannot read EC firmware')
- hash_src = flashrom.get_verification_image(image)
- return hashlib.sha256(hash_src).hexdigest()
+ algorithm = FindAlgorithm('Ec', algorithm)
+ return algorithm(image)
-def UpdateGBB(old_bios, db_file, in_place=False):
+def GetKeyHash(key_blob, algorithm=None):
"""
- Updates firmware image GBB data according to given components database file.
-
- Returns a new bios file that is changed its GBB values from old_bios
- according to the fields in components.
+ Returns a hash of cryptographic key blob
Args:
- old_bios: BIOS file to be changed its GBB values.
- components: hardware component list to be referred.
+ key_blob: Cryptographic key blob, with paddings.
+ algorithm: The algorithm to generate hash value
+ """
+ algorithm = FindAlgorithm('Key', algorithm)
+ return algorithm(key_blob)
+
+
+def UpdateGBB(old_image, db_file, in_place=False):
+ """
+ Updates main firmware image GBB data by given components database file.
+
+ Returns a new image with updated GBB data.
+
+ Args:
+ old_image: Firmware image file to be updated.
+ components: Hardware component list to be referred.
"""
base = gft_common.GetComponentsDatabaseBase(db_file)
try:
components = gft_common.LoadComponentsDatabaseFile(db_file)
except:
ErrorDie('UpdateGBB: Invalid components list file: %s' % db_file)
- for key in ['part_id_hwqual', 'data_bitmap_fv', 'key_root', 'key_recovery']:
- if len(components[key]) != 1 or components[key][0] == '*':
- ErrorDie('Components list should have a valid value for %s: %s' %
- (key, db_file))
- cmd = 'gbb_utility --set'
- cmd += ' --hwid="%s"' % components['part_id_hwqual'][0]
- cmd += ' --bmpfv="%s"' % os.path.join(base, components['data_bitmap_fv'][0])
- cmd += ' --rootkey="%s"' % os.path.join(base, components['key_root'][0])
- cmd += ' --recoverykey="%s"' % os.path.join(base,
- components['key_recovery'][0])
- cmd += ' %s' % old_bios
- new_bios = old_bios
+
+ # The are 2 fields in component list related to gbb:
+ # - part_id_hwqual (HWID, mandatory)
+ # - data_bitmap_fv (optional because we have universal bitmap now)
+ # TODO(hungte) check if the keys match "hash_key_*" in components
+ hwid = components.get('part_id_hwqual', [])
+ bmpfv = components.get('data_bitmap_fv', [])
+ if len(hwid) != 1:
+ ErrorDie('HWID (part_id_hwqual) must be one valid value.')
+ if len(bmpfv) != 1:
+ ErrorDie('bitmap')
+
+ hwid = hwid[0]
+ bmpfv = bmpfv[0]
+
+ cmd = 'gbb_utility --set --hwid="%s"' % hwid
+ if bmpfv:
+ cmd += ' --bmpfv="%s"' % os.path.join(base, bmpfv)
+ cmd += ' "%s"' % old_image
+ new_image = old_image
if not in_place:
- new_bios = gft_common.GetTemporaryFileName()
- cmd += ' %s' % new_bios
- cmd += ' >/dev/null'
+ new_image = gft_common.GetTemporaryFileName()
+ cmd += ' "%s"' % new_image
VerboseMsg("WriteGBB: invoke command: " + cmd)
- gft_common.SystemOutput(cmd)
- return new_bios
+ gft_common.System(cmd)
+ return new_image
#############################################################################
# Console main entry
@gft_common.GFTConsole
def _main():
- usage = 'Usage: %prog --target=BIOS|EC --image=IMAGE [--gbb=COMPONENTS]'
+ valid_targets = ['main', 'ec', 'gbb', 'gbbkeys']
+ usage = 'Usage: %prog --target=TARGET --image=IMAGE [--gbb=COMPONENTS]'
parser = optparse.OptionParser(usage=usage)
- parser.add_option('--target', dest='target', metavar='BIOS|EC',
- help='hash target, BIOS or EC')
+ parser.add_option('--target', dest='target', metavar='|'.join(valid_targets),
+ help=('type of target: %s' % ', '.join(valid_targets)))
parser.add_option('--image', dest='image',
help='firmware image file, or empty to read from system')
+ parser.add_option('--algorithm', dest='algorithm',
+ help='algorithm to generate hash')
parser.add_option('--gbb', dest='gbb', metavar='COMPONENTS',
help='component db file for replacing GBB data in BIOS')
(options, args) = parser.parse_args()
@@ -134,21 +290,36 @@
parser.error("Unknown param(s): " + ' '.join(args))
target = options.target and options.target.lower()
- if target not in ['bios', 'ec']:
- parser.error("Please specify either BIOS or EC for --target")
-
+ # legacy support
+ if target == 'bios':
+ target = 'main'
+ if target not in valid_targets:
+ #TODO(hungte) Detect type by FMAP section names
+ parser.error("Please specify --target from: %s" %
+ (', ').join(valid_targets))
modified_image = None
if options.gbb:
- if target != 'bios':
- parser.error("Please set --target=BIOS if replace GBB")
+ if target != 'main' and target != 'gbb':
+ parser.error("Please set --target='main' or 'gbb' to update GBB")
if image == '':
- parser.error("Please specify --image to a file if replace GBB")
+ parser.error("Please specify --image to a file to update GBB")
modified_image = UpdateGBB(image, options.gbb, in_place=False)
- if target == 'bios':
- print GetBIOSReadOnlyHash(modified_image or image)
+ algorithm = options.algorithm
+ if target == 'main':
+ print "Main Firmware: %s" % GetMainFirmwareReadOnlyHash(
+ modified_image or image, algorithm=algorithm)
+ elif target == 'gbb':
+ print "GBB Hash: %s" % GetMainFirmwareGbbHash(
+ image, algorithm=algorithm)
+ elif target == 'gbbkeys':
+ print "Recovery Key: %s" % GetKeyHash(
+ GetGbbKey(image, "recoverykey"), algorithm=algorithm)
+ print "Root Key: %s" % GetKeyHash(
+ GetGbbKey(image, "rootkey"), algorithm=algorithm)
elif target == 'ec':
- print GetECHash(image)
+ print "EC Firmware: %s" % GetEcFirmwareReadOnlyHash(
+ image, algorithm=algorithm)
# Remove the temporary GBB-modified file.
if modified_image:
diff --git a/gft_hwcomp.py b/gft_hwcomp.py
index e67ef8f..eafd93b 100755
--- a/gft_hwcomp.py
+++ b/gft_hwcomp.py
@@ -4,6 +4,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import array
+import fcntl
import glob
import imp
import os
@@ -11,7 +13,10 @@
import re
import sys
import threading
+import time
+import types
+import edid
import flashrom_util
import gft_common
import gft_fwhash
@@ -26,7 +31,7 @@
# Function names in this class are used for reflection, so please don't change
# the function names even if they are not compliant to coding style guide.
- version = 6
+ version = 7
# We divide all component IDs (cids) into 5 categories:
# - enumerable: able to get the results by running specific commands;
@@ -36,14 +41,18 @@
_enumerable_cids = [
'data_display_geometry',
'data_release_board',
- 'hash_ec_firmware',
- 'hash_ro_firmware',
- 'part_id_3g',
+ 'hash_key_recovery',
+ 'hash_key_root',
+ 'hash_gbb',
+ 'hash_ro_ec_firmware',
+ 'hash_ro_main_firmware',
'part_id_audio_codec',
'part_id_bluetooth',
- 'part_id_chrontel',
+ 'part_id_camera',
+ 'part_id_cellular',
'part_id_chipset',
'part_id_cpu',
+ 'part_id_display_converter',
'part_id_display_panel',
'part_id_dram',
'part_id_ec_flash_chip',
@@ -53,22 +62,20 @@
'part_id_hwqual',
'part_id_keyboard',
'part_id_storage',
+ 'part_id_touchpad',
'part_id_tpm',
'part_id_usb_hosts',
'part_id_vga',
- 'part_id_webcam',
'part_id_wireless',
- 'vendor_id_touchpad',
- 'version_3g_firmware',
+ 'version_cellular_firmware',
'version_rw_firmware',
+ 'version_touchpad_firmware',
# Deprecated fields:
# 'part_id_gps',
# - GPS is currently not supported by OS and no way to probe it.
# We should enable it only when OS supports it.
]
_probable_cids = [
- 'key_recovery',
- 'key_root',
'part_id_cardreader',
]
_pure_data_cids = [
@@ -93,10 +100,14 @@
_failure_list = [_not_present, _no_match, '']
# Type id for connection management (compatible to flimflam)
- _type_3g = 'cellular'
+ _type_cellular = 'cellular'
_type_ethernet = 'ethernet'
_type_wireless = 'wifi'
+ # Type id for touchpad information
+ _type_id = 'id'
+ _type_firmware = 'firmware'
+
_flimflam_dir = '/usr/local/lib/flimflam/test'
def __init__(self, verbose=False):
@@ -216,8 +227,8 @@
path = {}
info = {}
info_attribute_names = {
- self._type_3g: ['Carrier', 'FirmwareRevision', 'HardwareRevision',
- 'ModelID', 'Manufacturer'],
+ self._type_cellular: ['Carrier', 'FirmwareRevision', 'HardwareRevision',
+ 'ModelID', 'Manufacturer'],
}
devices = flimflam.FlimFlam().GetObjectList('Device')
unpack = flimflam.convert_dbus_value
@@ -247,8 +258,8 @@
connection_info = {
self._type_wireless: '/sys/class/net/wlan0/device',
self._type_ethernet: '/sys/class/net/eth0/device',
- # 3g(cellular) may also be /sys/class/net/usb0
- self._type_3g: '/sys/class/tty/ttyUSB0/device',
+ # cellular may also be /sys/class/net/usb0
+ self._type_cellular: '/sys/class/tty/ttyUSB0/device',
}
(path, _) = self.load_flimflam()
if path is not None:
@@ -257,6 +268,58 @@
connection_info[k] = (path[k] if k in path else '')
return connection_info
+ @Memorize
+ def i2cdump(self, bus, address, size):
+ """ Reads binary dump from i2c bus. """
+ # TODO(hungte) Use /usr/sbin/i2cdump if possible
+ self.load_module('i2c_dev')
+ if type(bus) is types.IntType:
+ bus = '/dev/i2c-%d' % bus
+ fd = -1
+ I2C_SLAVE = 0x0703
+ blob = None
+ try:
+ fd = os.open(bus, os.O_RDWR)
+ if fcntl.ioctl(fd, I2C_SLAVE, address) != 0:
+ return blob
+ time.sleep(0.05) # Wait i2c to get ready
+ if os.write(fd, chr(0)) == 1:
+ blob = os.read(fd, size)
+ except:
+ pass
+ finally:
+ if fd >= 0:
+ os.close(fd)
+ return blob
+
+ @Memorize
+ def load_lvds_edid(self):
+ """ Probes LVDS data by using EDID method. """
+ lvds_files = glob.glob('/sys/class/drm/*LVDS*/edid')
+ if lvds_files:
+ try:
+ return edid.parse_edid_file(lvds_files[0])
+ except ValueError, e:
+ ErrorMsg('EDID Parsing Error: %s' % e)
+
+ # Try i2c
+ self.load_module('i2c_dev')
+ i2c_files = glob.glob('/dev/i2c-?')
+ i2c_files.sort()
+ # LVDS are usually on address 0x50.
+ I2C_LVDS_ADDERSS = 0x50
+ for i2c_file in i2c_files:
+ edid_blob = self.i2cdump(i2c_file, I2C_LVDS_ADDERSS,
+ edid.EDID_MINIMAL_SIZE)
+ if edid_blob:
+ try:
+ return edid.parse_edid(edid_blob)
+ except ValueError, e:
+ WarningMsg('EDID Parsing Error: %s' % e)
+
+ # TODO(hungte) we can also try xrandr
+ return None
+
def _get_sysfs_device_info(self, path, primary, optional=[]):
"""Gets the device information of a sysfs node.
@@ -299,7 +362,7 @@
if os.path.exists(os.path.join(path, 'idProduct')):
break
path = os.path.split(path)[0]
- optional_fields = ['manufacturer', 'product']
+ optional_fields = ['manufacturer', 'product', 'bcdDevice']
(info, optional) = self._get_sysfs_device_info(
path, ['idVendor', 'idProduct'], optional_fields)
if not info:
@@ -465,45 +528,158 @@
return value
# --------------------------------------------------------------------
+ # Product-Specific Probing
+
+ @Memorize
+ def probe_touchpad(self):
+ """ Probes touchpad information.
+
+ Returns:
+ A dict of { _type_id: 'TOUCHPAD_HWINFO',
+ _type_firmware: 'TOUCHPAD_FWINFO' }
+ """
+
+ def synaptics():
+ detect_program = '/opt/Synaptics/bin/syndetect'
+ model_string_str = 'Model String'
+ firmware_id_str = 'Firmware ID'
+
+ if not os.path.exists(detect_program):
+ return None
+ command_list = [detect_program]
+ # Determine if X is capturing touchpad
+ locked = os.system('lsof /dev/serio_raw0 2>/dev/null | grep -q "^X"')
+
+ if (locked == 0) and (not os.getenv('DISPLAY')):
+ ErrorMsg('Warning: You are trying to detect touchpad with X in '
+ 'foreground but not configuring DISPLAY properly.\n'
+ 'Test may fail with incorrect detection results.')
+ # Make a trial with default configuration (see cros/cros_ui.py and
+ # suite_Factory/startx.sh)
+ command_list.insert(0, 'DISPLAY=":0"')
+ xauthority_locations = ('/var/run/factory_ui.auth',
+ '/home/chronos/.Xauthority')
+ valid_xauth = [xauth for xauth in xauthority_locations
+ if os.path.exists(xauth)]
+ if valid_xauth:
+ command_list.insert(0, 'XAUTHORITY="%s"' % valid_xauth[0])
+
+ (exit_code, data, _) = gft_common.ShellExecution(
+ ' '.join(command_list),
+ ignore_status=True,
+ progress_message='Synaptics Touchpad: ',
+ show_progress=self._verbose)
+ if exit_code != 0:
+ return None
+ properties = dict(map(str.strip, line.split('=', 1))
+ for line in data.splitlines() if '=' in line)
+ model = properties.get(model_string_str, 'Unknown Synaptics')
+ # The pattern " on xxx Port" may vary by the detection approach,
+ # so we need to strip it.
+ model = re.sub(' on [^ ]* [Pp]ort$', '', model)
+ firmware_id = properties.get(firmware_id_str, self._not_present)
+ return (model, firmware_id)
+
+ def cypress():
+ nodes = glob.glob('/sys/class/input/mouse[0-9]*/device/device')
+ for node in nodes:
+ id_files = ['product_id', 'hardware_version', 'protocol_version']
+ if not all([os.path.exists(os.path.join(node, field))
+ for field in id_files]):
+ continue
+ firmware_files = ['firmware_version']
+ model = self.compact_id(
+ [gft_common.ReadOneLine(os.path.join(node, field))
+ for field in id_files])
+ firmware_id = self.compact_id(
+ [gft_common.ReadOneLine(os.path.join(node, field))
+ for field in firmware_files
+ if os.path.exists(os.path.join(node, field))])
+ return (model, firmware_id)
+ return None
+
+ def generic():
+ # TODO(hungte) add more information from id/*
+ # format: N: Name="XXX_trackpad"
+ input_file = '/proc/bus/input/devices'
+ cmd_grep = 'grep -iE "^N.*(touch *pad|track *pad)" %s' % input_file
+ info = gft_common.SystemOutput(cmd_grep, ignore_status=True).splitlines()
+ info = [re.sub('^[^"]*"(.*)"$', r'\1', device)
+ for device in info]
+ return (', '.join(info) or self._not_present, self._not_present)
+
+ method_list = [cypress, synaptics, generic]
+ data = { self._type_id: self._not_present,
+ self._type_firmware: self._not_present }
+ for method in method_list:
+ result = method()
+ DebugMsg('probe_touchpad: %s: %s' % (method,result or '<failed>'))
+ if result:
+ data[self._type_id] = result[0]
+ data[self._type_firmware] = result[1]
+ return data
+ return data
+
+ # --------------------------------------------------------------------
# Enumerable Properties
def get_data_display_geometry(self):
# Get edid from driver.
# TODO(nsanders): this is driver specific.
- # TODO(waihong): read-edid is also x86 only.
- # format: Mode "1280x800" -> 1280x800
- # TODO(hungte) merge this with get_part_id_display_panel
+ # Try EDID
+ lvds_edid = self.load_lvds_edid()
+ if lvds_edid:
+ return '%dx%d' % (lvds_edid[edid.EDID_WIDTH],
+ lvds_edid[edid.EDID_HEIGHT])
- cmd = ('cat "$(find /sys/devices/ -name edid | grep LVDS)" | '
- 'parse-edid 2>/dev/null | grep "Mode " | cut -d\\" -f2')
- output = gft_common.SystemOutput(cmd).splitlines()
- return (output if output else [''])
+ # Try frame buffer
+ fb_modes_file = '/sys/class/graphics/fb0/modes'
+ if os.path.exists(fb_modes_file):
+ # format: U:1366x768p-0
+ fb_mode = gft_common.ReadOneLine(fb_modes_file)
+ geometry = re.search(r'\d+x\d+', fb_mode)
+ if geometry:
+ return geometry.group(0)
+
+ return self._not_present
+
+ def get_hash_gbb(self):
+ image_file = self.load_main_firmware()
+ return gft_fwhash.GetMainFirmwareGbbHash(file_source=image_file)
+
+ def get_hash_key_recovery(self):
+ current_key = self._read_gbb_component('recoverykey')
+ return gft_fwhash.GetKeyHash(current_key)
+
+ def get_hash_key_root(self):
+ current_key = self._read_gbb_component('rootkey')
+ return gft_fwhash.GetKeyHash(current_key)
@EcProperty
- def get_hash_ec_firmware(self):
+ def get_hash_ro_ec_firmware(self):
"""
- Returns a hash of Embedded Controller firmware parts,
+ Returns a hash of Embedded Controller firmware read only parts,
to confirm we have proper updated version of EC firmware.
"""
image_file = self.load_ec_firmware()
if not image_file:
- ErrorDie('get_hash_ec_firmware: cannot read firmware')
- return gft_fwhash.GetECHash(file_source=image_file)
+ ErrorDie('get_hash_ro_ec_firmware: cannot read firmware')
+ return gft_fwhash.GetEcFirmwareReadOnlyHash(file_source=image_file)
- def get_hash_ro_firmware(self):
+ def get_hash_ro_main_firmware(self):
"""
- Returns a hash of Read Only (BIOS) firmware parts,
+ Returns a hash of main firmware (BIOS) read only parts,
to confirm we have proper keys / boot code / recovery image installed.
"""
image_file = self.load_main_firmware()
if not image_file:
- ErrorDie('get_hash_ro_firmware: cannot read firmware')
- return gft_fwhash.GetBIOSReadOnlyHash(file_source=image_file)
+ ErrorDie('get_hash_ro_main_firmware: cannot read firmware')
+ return gft_fwhash.GetMainFirmwareReadOnlyHash(file_source=image_file)
- def get_part_id_3g(self):
- device_path = self._get_all_connection_info()[self._type_3g]
+ def get_part_id_cellular(self):
+ device_path = self._get_all_connection_info()[self._type_cellular]
return self.get_sysfs_device_id(device_path) or self._not_present
def get_part_id_audio_codec(self):
@@ -511,31 +687,80 @@
part_id = gft_common.SystemOutput(
cmd, progress_message='Searching Audio Codecs: ',
show_progress=self._verbose).strip()
+ # If no codec installed, try PCM.
+ if not part_id:
+ # format: 00-00: WMXXXX PCM wmxxxx-hifi-0: ...
+ pcm_data = gft_common.ReadOneLine('/proc/asound/pcm').split(' ')
+ if len(pcm_data) > 2:
+ part_id = pcm_data[1]
return part_id
def get_part_id_bluetooth(self):
return self.get_sysfs_device_id('/sys/class/bluetooth/hci0/device')
- def get_part_id_chrontel(self):
- """ Gets chrontel HDMI devices by dedicated probing """
+ def get_part_id_camera(self):
+ info = []
+ camera_node = '/sys/class/video4linux/video0'
+ info.append(self.get_sysfs_node_id(camera_node))
+ # For soc-camera, "control/name" is a helpful driver name.
+ control_name = os.path.join(camera_node, 'device/control/name')
+ if os.path.exists(control_name):
+ info.append(gft_common.ReadOneLine(control_name))
+ # Try video4linux2 (v4l2) interface
+ camera_dev = '/dev/video0'
+ if os.path.exists(camera_dev):
+ fd = -1
+ try:
+ fd = os.open(camera_dev, os.O_RDWR)
+ # See /usr/include/linux/videodev2.h for definition
+ VIDIOC_DBG_G_CHIP_IDENT = 0xc02c5651
+ V4L2_DBG_CHIP_IDENT_SIZE = 11
+ V4L2_INDEX_REVISION = V4L2_DBG_CHIP_IDENT_SIZE - 1
+ V4L2_INDEX_IDENT = V4L2_INDEX_REVISION - 1
+ V4L2_VALID_IDENT = 3 # V4L2_IDENT_UNKNOWN + 1
+ v4l2_dbg_chip_ident = array.array('i', [0] * V4L2_DBG_CHIP_IDENT_SIZE)
+ fcntl.ioctl(fd, VIDIOC_DBG_G_CHIP_IDENT, v4l2_dbg_chip_ident, 1)
+ # 'ident' values are defined in include/media/v4l2-chip-ident.h
+ v4l2_ident = v4l2_dbg_chip_ident[V4L2_INDEX_IDENT]
+ if v4l2_ident >= V4L2_VALID_IDENT:
+ info.append('V4L2:%04x %04x' %
+ (v4l2_ident, v4l2_dbg_chip_ident[V4L2_INDEX_REVISION]))
+ except:
+ pass
+ finally:
+ if fd >= 0:
+ os.close(fd)
+ return self.compact_id(info)
+
+ def get_part_id_display_converter(self):
+ """ Gets display converter by dedicated probing """
def probe_ch7036():
self.load_module('i2c_dev')
probe_cmd = 'ch7036_monitor -p >/dev/null 2>&1'
return 'ch7036' if os.system(probe_cmd) == 0 else ''
method_list = [probe_ch7036]
- part_id = ''
+ part_id = self._not_present
for method in method_list:
part_id = method()
- DebugMsg('get_part_id_chrontel: %s: %s' %
+ DebugMsg('get_part_id_display_converter: %s: %s' %
(method.__name__, part_id or '<failed>'))
if part_id:
break
return part_id
def get_part_id_chipset(self):
- # Host bridge is always the first PCI device.
- return self.get_sysfs_device_id('/sys/bus/pci/devices/0000:00:00.0')
+ # On x86, host bridge is always the first PCI device.
+ # For SOC-based system, trust the first compatible list in device-tree
+ # (fdt).
+ part_id = self.get_sysfs_device_id('/sys/bus/pci/devices/0000:00:00.0')
+ fdt_compatible_file = '/proc/device-tree/compatible'
+ if (not part_id) and os.path.exists(fdt_compatible_file):
+ compatible_list = gft_common.ReadOneLine(fdt_compatible_file)
+ # format: manufacturer,model [NUL] compat-manufacturer,model [NUL] ...
+ info = compatible_list.strip(chr(0)).split(chr(0))
+ part_id = self.compact_id(info)
+ return part_id
def get_part_id_cpu(self):
part_id = 'Unknown'
@@ -571,23 +796,34 @@
return self.compact_id(info)
def get_part_id_display_panel(self):
- # format: ModelName "SEC:4231" -> SEC:4231
+ # Try EDID
+ lvds_edid = self.load_lvds_edid()
+ if lvds_edid:
+ return '%s:%04x' % (lvds_edid[edid.EDID_MANUFACTURER_ID],
+ lvds_edid[edid.EDID_PRODUCT_ID])
+ # Try frame buffer
+ fb_filename = '/sys/class/graphics/fb0/name'
+ if os.path.exists(fb_filename):
+ return gft_common.ReadOneLine(fb_filename)
- cmd = ('cat "$(find /sys/devices/ -name edid | grep LVDS)" | '
- 'parse-edid 2>/dev/null | grep ModelName | cut -d\\" -f2')
- output = gft_common.SystemOutput(cmd).strip()
- return (output if output else self._not_present)
+ return self._not_present
def get_part_id_dram(self):
- # TODO(hungte) if we only want DRAM size, maybe no need to use mosys
- self.load_module('i2c_dev')
- cmd = ('mosys -l memory spd print geometry | '
- 'grep size_mb | cut -f2 -d"|"')
- part_id = gft_common.SystemOutput(cmd).strip()
- if part_id != '':
- return part_id
- else:
- return self._not_present
+ arch = self.get_arch()
+ if arch in ('x86', 'amd64'):
+ # TODO(hungte) if we only want DRAM size, maybe no need to use mosys
+ self.load_module('i2c_dev')
+ cmd = ('mosys -l memory spd print geometry | '
+ 'grep size_mb | cut -f2 -d"|"')
+ part_id = gft_common.SystemOutput(cmd).strip()
+ elif arch in ('arm'):
+ # Even kernel cannot probe the memory. We can only trust the memory
+ # information passed to kernel.
+ param = gft_common.ReadOneLine('/proc/cmdline')
+ # Format: *mem=384M@0M (type=size@address)
+ part_id = '%d' % sum(
+ [int(size) for size in re.findall(r'\s\w*mem=(\d+)M@\d+M', param)])
+ return part_id if part_id else self._not_present
@EcProperty
def get_part_id_ec_flash_chip(self):
@@ -622,15 +858,19 @@
def get_part_id_hwqual(self):
part_id = gft_common.SystemOutput('crossystem hwid').strip()
+ # TODO(hungte) compare this with HWID in GBB.
return (part_id if part_id else self._not_present)
def get_part_id_storage(self):
- part_id = ''
- path = gft_common.SystemOutput('find /sys/devices -name "%s"' %
- self.get_ssd_name())
- path = os.path.join(path.strip(), 'device')
+ part_id = self._not_present
+ node = '/sys/class/block/%s' % self.get_ssd_name()
+ path = os.path.join(node, 'device')
if not os.path.exists(path):
return part_id
+ size = ''
+ size_path = os.path.join(node, 'size')
+ if os.path.exists(size_path):
+ size = '#' + gft_common.ReadOneLine(size_path)
info_list = (
['vendor', 'model'], # ATA
['type', 'name', 'fwrev', 'hwrev', 'oemid', 'manfid'], # EMMC
@@ -640,7 +880,7 @@
for data_file in info
if os.path.exists(os.path.join(path, data_file))]
if data:
- return self.compact_id(data)
+ return self.compact_id(data + [size])
return part_id
def get_part_id_keyboard(self):
@@ -651,16 +891,20 @@
image_file).strip()
return part_id or self._not_present
+ def get_part_id_touchpad(self):
+ data = self.probe_touchpad()
+ return data[self._type_id]
+
def get_part_id_tpm(self):
""" Returns Manufacturer_info : Chip_Version """
cmd = 'tpm_version'
+ part_id = self._not_present
tpm_output = gft_common.SystemOutput(cmd)
tpm_lines = tpm_output.splitlines()
tpm_dict = {}
for tpm_line in tpm_lines:
[key, colon, value] = tpm_line.partition(':')
tpm_dict[key.strip()] = value.strip()
- part_id = ''
(key1, key2) = ('Manufacturer Info', 'Chip Version')
if key1 in tpm_dict and key2 in tpm_dict:
part_id = tpm_dict[key1] + ':' + tpm_dict[key2]
@@ -687,92 +931,20 @@
def get_part_id_vga(self):
return self.get_sysfs_node_id('/sys/class/graphics/fb0')
- def get_part_id_webcam(self):
- return self.get_sysfs_node_id('/sys/class/video4linux/video0')
-
def get_part_id_wireless(self):
device_path = self._get_all_connection_info()[self._type_wireless]
return self.get_sysfs_device_id(device_path) or self._not_present
- def get_vendor_id_touchpad_synaptics(self):
- part_id = ''
- detect_program = '/opt/Synaptics/bin/syndetect'
- model_string_str = 'Model String'
- firmware_id_str = 'Firmware ID'
-
- if not os.path.exists(detect_program):
- return part_id
-
- command_list = [detect_program]
-
- # Determine if X is capturing touchpad
- locked = os.system('lsof /dev/serio_raw0 2>/dev/null | grep -q "^X"')
- if (locked == 0) and (not os.getenv('DISPLAY')):
- ErrorMsg('Warning: You are trying to detect touchpad with X in foreground'
- ' but not configuring DISPLAY properly for this test.\n'
- 'Test may fail with incorrect detection results.')
- # Make a trial with default configuration (see cros/cros_ui.py and
- # suite_Factory/startx.sh)
- command_list.insert(0, 'DISPLAY=":0"')
- xauthority_locations = ('/var/run/factory_ui.auth',
- '/home/chronos/.Xauthority')
- valid_xauth = [xauth for xauth in xauthority_locations
- if os.path.exists(xauth)]
- if valid_xauth:
- command_list.insert(0, 'XAUTHORITY="%s"' % valid_xauth[0])
-
- (exit_code, data, _) = gft_common.ShellExecution(
- ' '.join(command_list),
- ignore_status=True,
- progress_message='Synaptics Touchpad: ',
- show_progress=self._verbose)
- if exit_code != 0:
- return part_id
-
- properties = dict(map(str.strip, line.split('=', 1))
- for line in data.splitlines() if '=' in line)
- model = properties.get(model_string_str, 'UnknownModel')
- firmware_id = properties.get(firmware_id_str, 'UnknownFWID')
-
- # The pattern " on xxx Port" may vary by the detection approach,
- # so we need to strip it.
- model = re.sub(' on [^ ]* [Pp]ort$', '', model)
-
- # Format: Model #FirmwareId
- part_id = '%s #%s' % (model, firmware_id)
- return part_id
-
- def get_vendor_id_touchpad_generic(self):
- # TODO(hungte) add more information from id/*
- # format: N: Name="XXX_trackpad"
- cmd_grep = 'grep -iE "^N.*(touch *pad|track *pad)" /proc/bus/input/devices'
- info = gft_common.SystemOutput(cmd_grep, ignore_status=True).splitlines()
- info = [re.sub('^[^"]*"(.*)"$', r'\1', device)
- for device in info]
- return ', '.join(info) or self._not_present
-
- @Memorize
- def get_vendor_id_touchpad(self):
- method_list = ['synaptics', 'generic']
- part_id = ''
- for method in method_list:
- part_id = getattr(self, 'get_vendor_id_touchpad_%s' % method)()
- DebugMsg('get_vendor_id_touchpad: %s: %s' %
- (method, part_id or '<failed>'))
- if part_id:
- break
- return part_id
-
- def get_version_3g_firmware(self):
+ def get_version_cellular_firmware(self):
(_, attributes) = self.load_flimflam()
- if attributes and (self._type_3g in attributes):
+ if attributes and (self._type_cellular in attributes):
# A list of possible version info combination, since the supported fields
# may differ for partners.
version_formats = [
['Carrier', 'FirmwareRevision'],
['FirmwareRevision'],
['HardwareRevision']]
- info = attributes[self._type_3g]
+ info = attributes[self._type_cellular]
for version_format in version_formats:
if not set(version_format).issubset(set(info)):
continue
@@ -794,6 +966,7 @@
firmwar update so return value is a "error report" in the form "A=x, B=y".
"""
+ # TODO(hungte) Support new VBLOCK, or use vbutil_firmware
versions = [None, None]
section_names = ['VBLOCK_A', 'VBLOCK_B']
image_file = self.load_main_firmware()
@@ -818,21 +991,13 @@
return 'A=%d, B=%d' % (versions[0], versions[1])
return '%d' % versions[0]
+ def get_version_touchpad_firmware(self):
+ data = self.probe_touchpad()
+ return data[self._type_firmware]
+
# --------------------------------------------------------------------
# Probable Properties
- def probe_key_recovery(self, part_id):
- current_key = self._read_gbb_component('recoverykey')
- file_path = os.path.join(self._file_base, part_id)
- target_key = gft_common.ReadBinaryFile(file_path)
- return current_key.startswith(target_key)
-
- def probe_key_root(self, part_id):
- current_key = self._read_gbb_component('rootkey')
- file_path = os.path.join(self._file_base, part_id)
- target_key = gft_common.ReadBinaryFile(file_path)
- return current_key.startswith(target_key)
-
def probe_part_id_cardreader(self, part_id):
# A cardreader is always power off until a card inserted. So checking
# it using log messages instead of lsusb can limit operator-attended.
diff --git a/gft_prepare_wipe.sh b/gft_prepare_wipe.sh
index 86ba328..f5e8266 100755
--- a/gft_prepare_wipe.sh
+++ b/gft_prepare_wipe.sh
@@ -32,6 +32,7 @@
enable_kernel() {
alert "Enabling kernel on $CGPT_DEVICE #$1 ..."
cgpt add -i "$1" -P "$ENABLED_KERNEL_PRIORITY" -S 1 -T 0 "$CGPT_DEVICE"
+ cgpt prioritize -i "$1" -P "$ENABLED_KERNEL_PRIORITY" "$CGPT_DEVICE"
}
# usage: disable_kernel partition_no
diff --git a/gooftool b/gooftool
index b4a3c71..53fb3a0 100755
--- a/gooftool
+++ b/gooftool
@@ -68,10 +68,31 @@
return g_hwcomp
-def FindComponentsDatabases(db_path_pattern):
- """ Finds files by pattern for component list databases. """
- files = glob.glob(db_path_pattern)
- return files
+@gft_common.Memorize
+def FindComponentsDatabases():
+ """ Finds component database files. If --db_path is assigned, use the
+ specified path; otherwise look for default folders in system. """
+ db_path = g_options.db_path
+ if db_path:
+ files = glob.glob(db_path)
+ if not files:
+ ErrorDie('No components database found in %s.' % db_path)
+ return files
+
+ # find in default path
+ default_db_paths = [
+ '/usr/local/share/chromeos-hwid',
+ '/usr/share/chromeos-hwid',
+ ]
+ for db_path in default_db_paths:
+ files = glob.glob(os.path.join(db_path, 'components*'))
+ if files:
+ VerboseMsg('Loading components database from %s' % db_path)
+ return files
+
+ # found nothing
+ ErrorDie('No components database found. You must assign --db_path.')
+ return None
def VerifySwitch(name, value):
@@ -88,31 +109,38 @@
return True
-def MatchComponentsDatabases(db_path):
+def MatchComponentsDatabases(db_list):
"""Tries to match current system to known qualified component lists.
Args:
- db_path: path (or pattern) to the component list database files.
+ db_list: list of component list database files.
Returns:
- A list of matched component lists in dict form: { db_file: matched_list }.
+ Tuple of (matched, unmatched, unrelated)
+ matched: Matched component lists in dict form: { db_file: matched }.
+ unmatched: Lists matched part_id_hwqual but failed in other components.
+ unrelated: Those part_id_hwqual does not match.
"""
- if not db_path:
- ErrorDie('VerifyComponents: need --db_path.')
- db_list = FindComponentsDatabases(db_path)
hwcomp = InitializeHardwareComponents()
if not hwcomp:
ErrorDie('VerifyComponents: failed to detect system configuration.')
- matched_list = {}
+ matched = {}
+ unmatched = {}
+ unrelated = {}
for db in db_list:
VerboseMsg('MatchComponentsDatabases: Matching current system by %s' % db)
- (matched, failure) = hwcomp.match_current_system(db)
+ (match, failure) = hwcomp.match_current_system(db)
if failure:
- DebugMsg('Unmatched for %s:%s\n' % (db, hwcomp.pformat(failure)))
+ if 'part_id_hwqual' in failure:
+ unrelated[db] = failure
+ DebugMsg('Unrelated for %s:%s\n' % (db, hwcomp.pformat(failure)))
+ else:
+ unmatched[db] = failure
+ DebugMsg('Unmatched for %s:%s\n' % (db, hwcomp.pformat(failure)))
else:
- matched_list[db] = matched
+ matched[db] = match
VerboseMsg('Matched: %s' % db)
- return matched_list
+ return (matched, unmatched, unrelated)
def EnableWriteProtect(target, image):
@@ -124,6 +152,9 @@
""" Writes a prepared GBB data to system main firwmare. """
flashrom = flashrom_util.flashrom_util(verbose_msg=VerboseMsg,
exception_type=gft_common.GFTError)
+ # TODO(hungte) Check if current keys in GBB match component list, to prevent
+ # installing wrong GBB data to systems designed for incompatile keys.
+ # TODO(hungte) use new flashrom partial read/write syntax to speed up
flashrom.select_target('bios')
image_file = gft_common.GetTemporaryFileName('wgbb')
if not flashrom.read_whole_to_file(image_file):
@@ -212,15 +243,16 @@
""" Probes hardware components on current system and find match to given
component database files for HWID.
"""
- if not g_options.db_path:
- ErrorDie('probe: need --db_path.')
- db_list = FindComponentsDatabases(g_options.db_path)
+ db_list = FindComponentsDatabases()
hwcomp = InitializeHardwareComponents(do_probe=True)
matched_hwids = 0
matched = {}
# Don't care about GBB elements when probing
- ignored_cids = ['hash_ro_firmware', 'part_id_hwqual', 'version_rw_firmware']
+ ignored_cids = ['hash_ro_main_firmware',
+ 'hash_gbb',
+ 'part_id_hwqual',
+ 'version_rw_firmware']
for db in db_list:
VerboseMsg('probe: Matching current system by %s' % db)
(matched, failure) = hwcomp.match_current_system(db,
@@ -248,15 +280,20 @@
def verify_hwid():
""" Verifies if system HWID matches the qualified component list. """
hwcomp = InitializeHardwareComponents()
- matched_list = MatchComponentsDatabases(g_options.db_path)
- matched = len(matched_list)
- if matched > 0:
- DebugMsg('Current System:%s\n' % hwcomp.pformat(matched_list.values()[0]))
- if matched != 1:
- ErrorDie('verify_hwid: failed to match exact one HWID (found %d).' %
- matched)
- matched_filename = matched_list.keys()[0]
- matched_data = matched_list[matched_filename]
+ (matched, unmatched, unrelated) = (
+ MatchComponentsDatabases(FindComponentsDatabases()))
+ error_msg = ''
+ if unmatched:
+ error_msg += '\nUnmatched items:\n'
+ error_msg += '\n'.join(['%s:%s' % (name, hwcomp.pformat(value))
+ for name, value in unmatched.items()])
+ if matched:
+ DebugMsg('Current System:%s\n' % hwcomp.pformat(matched.values()[0]))
+ if len(matched) != 1:
+ ErrorDie('verify_hwid: failed to match exact one HWID (found %d).%s' %
+ (len(matched), error_msg))
+ matched_filename = matched.keys()[0]
+ matched_data = matched[matched_filename]
matched_hwid = matched_data['part_id_hwqual'][0]
print 'Verified: %s (%s)' % (matched_filename, matched_hwid)
return True
@@ -342,7 +379,8 @@
# TODO(hungte) if matching is still too slow, we can try to reused previous
# cached results.
- matched = MatchComponentsDatabases(g_options.db_path)
+ (matched, unmatched, unrelated) = (
+ MatchComponentsDatabases(FindComponentsDatabases()))
if not len(matched) == 1:
ErrorDie('create_report: HWID is not singularly matched to database.')
diff --git a/vblock.py b/vblock.py
index 5c3fa8e..f9a3caa 100644
--- a/vblock.py
+++ b/vblock.py
@@ -52,8 +52,7 @@
raise ValueError, 'unknown key block magic: %s' % header['magic']
major = header['header_version_major']
minor = header['header_version_minor']
- if major != KEY_BLOCK_HEADER_VERSION_MAJOR or \
- minor != KEY_BLOCK_HEADER_VERSION_MINOR:
+ if (major != KEY_BLOCK_HEADER_VERSION_MAJOR):
raise ValueError, 'unknown key block version (%d.%d)' % (major, minor)
return header
@@ -87,8 +86,7 @@
# check values
major = header['header_version_major']
minor = header['header_version_minor']
- if major != FIRMWARE_PREAMBLE_HEADER_VERSION_MAJOR or \
- minor != FIRMWARE_PREAMBLE_HEADER_VERSION_MINOR:
+ if major != FIRMWARE_PREAMBLE_HEADER_VERSION_MAJOR:
raise ValueError, 'unknown preamble version: (%d.%d)' % (major, minor)
return header
@@ -114,11 +112,12 @@
def test_report_vblock_info(blob, offset=0):
""" Reports the information of a vblock blob. """
- kb_header = unpack_VbKeyBlockHeader(msg, offset)
+ kb_header = unpack_VbKeyBlockHeader(blob, offset)
print "-- VbKeyBlockHeader --"
for name, value in kb_header.items():
print name, ':', value
- preamble = unpack_VbFirmwarePreambleHeader(msg, kb_header['key_block_size'])
+ preamble = unpack_VbFirmwarePreambleHeader(blob,
+ kb_header['key_block_size'])
print "-- VbFirmwarePreambleHeader --"
for name, value in preamble.items():
print name, ':', value
@@ -129,4 +128,4 @@
if __name__ == "__main__":
# when running in command line, try to report blob in the parameters
for filename in sys.argv[1:]:
- test_report_blob_info(open(filename, "rb").read())
+ test_report_vblock_info(open(filename, "rb").read())