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())