bisect-kit: workaround cbi issue for volteer

See https://buganizer.corp.google.com/issues/183020319 for more details.

BUG=b:183567529
TEST=./switch_cros_prebuilt.py --session test --dut ${HOST} --board volteer R91-13837.0.0

Change-Id: I563ae0e17c3af5288e5bf17533f1c81dca452552
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/bisect-kit/+/2794347
Reviewed-by: Kuang-che Wu <kcwu@chromium.org>
Reviewed-by: Zhuohao Lee <zhuohao@chromium.org>
Commit-Queue: Zheng-Jie Chang <zjchang@chromium.org>
Tested-by: Zheng-Jie Chang <zjchang@chromium.org>
diff --git a/bisect_kit/cros_util.py b/bisect_kit/cros_util.py
index b45fbfc..8d579ad 100644
--- a/bisect_kit/cros_util.py
+++ b/bisect_kit/cros_util.py
@@ -1193,6 +1193,8 @@
           clobber_stateful=clobber_stateful,
           disable_rootfs_verification=disable_rootfs_verification,
           force_reboot_callback=force_reboot_callback)
+      workaround_b183567529(
+          host, board, version, force_reboot_callback=force_reboot_callback)
       return True
     except errors.ExternalError:
       logger.exception('cros flash failed')
@@ -1672,6 +1674,41 @@
   return image_folder
 
 
+def workaround_b183567529(host, board, version=None,
+                          force_reboot_callback=None):
+  """Workaround for volteer failure.
+
+  See b/183567529#comment8 and b/183020319#comment26 for more details.
+  """
+  broken_range = [
+      ('13836.0.0', '13854.0.0'),
+      ('13816.13.0', '13816.19.0'),
+  ]
+  if board != 'volteer' or not version:
+    return
+
+  if is_cros_short_version(version):
+    short_version = version
+  else:
+    _, short_version = version_split(version)
+  for old, new in broken_range:
+    if util.is_version_lesseq(old, short_version) and util.is_version_lesseq(
+        short_version,
+        new) and (util.is_direct_relative_version(old, short_version) and
+                  util.is_direct_relative_version(short_version, new)):
+      logger.info('applying b183567529 cbi patch for volteer')
+      cbi_override = os.path.join(
+          common.BISECT_KIT_ROOT,
+          'patching/b183567529/volteer-cbi-override.conf')
+      util.scp_cmd(cbi_override, 'root@%s:/etc/init/' % host)
+
+      patch_script = os.path.join(common.BISECT_KIT_ROOT,
+                                  'patching/b183567529/test_cbi_script.sh')
+      util.check_output(patch_script, host)
+      reboot(host, force_reboot_callback)
+      break
+
+
 class AutotestControlInfo:
   """Parsed content of autotest control file.
 
diff --git a/bisect_kit/util.py b/bisect_kit/util.py
index 2a1b75d..7f6dd99 100644
--- a/bisect_kit/util.py
+++ b/bisect_kit/util.py
@@ -272,6 +272,50 @@
       raise errors.SshConnectionError('ssh connection to %r failed' % host)
 
 
+def scp_cmd(from_file, to_file, connect_timeout=None, allow_retry=False):
+  """Copies a file through scp.
+
+  Args:
+    from_file: from file name
+    to_file: target file name
+    connect_timeout: connection timeout in seconds (int)
+    allow_retry: if True, retry when ssh connection failed
+
+  Raises:
+    subprocess.CalledProcessError if the exit code is non-zero.
+  """
+  cmd = ['scp']
+  # Avoid keyboard-interactive to prevent hang forever.
+  cmd += ['-oPreferredAuthentications=publickey']
+  if connect_timeout:
+    cmd += ['-oConnectTimeout=%d' % connect_timeout]
+  cmd += [
+      from_file,
+      to_file,
+  ]
+
+  tries = 0
+  while True:
+    tries += 1
+    try:
+      return check_output(*cmd)
+    except subprocess.CalledProcessError as e:
+      # ssh's own error code is 255. For other codes, they are returned from
+      # the remote command.
+      if e.returncode != 255:
+        raise
+      if allow_retry and tries < 3:
+        # ssh's ConnectionAttempts is not enough because we want to deal with
+        # situations needing several seconds to recover.
+        delay = 60
+        logger.warning('ssh connection failed, will retry %d seconds later',
+                       delay)
+        time.sleep(delay)
+        continue
+      raise errors.SshConnectionError('scp %s to %s failed' %
+                                      (from_file, to_file))
+
+
 def escape_rev(rev):
   """Escapes special characters in version string.
 
diff --git a/patching/b183567529/test_cbi_script.sh b/patching/b183567529/test_cbi_script.sh
new file mode 100755
index 0000000..8673ef0
--- /dev/null
+++ b/patching/b183567529/test_cbi_script.sh
@@ -0,0 +1,306 @@
+#!/bin/bash
+
+check_and_modify_fw_config() {
+
+  set -e
+  target="root@$1"
+
+  logit() {
+    echo "$@"
+  }
+
+  model="$(ssh ${target} cros_config / name)"
+  rev="$(ssh ${target} ectool cbi get 0 | grep 'As uint:' | cut -d' ' -f3)"
+  sku="$(ssh ${target} ectool cbi get 2 | grep 'As uint:' | cut -d' ' -f3)"
+  fw_config="$(ssh ${target} ectool cbi get 6 | grep 'As uint:' | cut -d' ' -f3)"
+  fw_type="$(ssh ${target} crossystem mainfw_type)"
+  wpsw_cur="$(ssh ${target} crossystem wpsw_cur)"
+  new_fw_config=0
+
+  # only apply to the normal mode devices and which fw_config is zero
+  # except copano and drobit
+  if [ "${fw_type}" != "normal" ] || \
+     [ "${fw_config}" -ne 0 -a \
+       "${model}" != "copano" -a \
+       "${model}" != "drobit" ]; then
+    logit "Exit 1"
+    exit 0
+  fi
+
+  # only apply to the board revision <= 2
+  if [ "${rev}" -gt 2 ]; then
+    exit 0
+  fi
+
+  # apply to copano, delbin, drobit
+  # eldrid, elemi, lillipup, lindar,
+  # voema, volteer2 and voxel
+  case ${model} in
+    copano)
+      if [ "${rev}" -eq 1 ]; then
+        case ${sku} in
+          917505)
+            new_fw_config=8604675
+            ;;
+          917506)
+            new_fw_config=42159107
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    delbin)
+      if [ "${rev}" -eq 2 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          65540)
+            new_fw_config=8456706
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    delbin_npcx796fc)
+      if [ "${rev}" -eq 1 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          65537|65538|65539)
+            new_fw_config=68098
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    drobit)
+      if [ "${rev}" -eq 1 ]; then
+        case ${sku} in
+          786433|786434|786435)
+            new_fw_config=8471043
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    eldrid)
+      if [ "${rev}" -eq 2 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          589841)
+            new_fw_config=8670466
+            ;;
+          589842)
+            new_fw_config=9719042
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    eldrid_npcx796)
+      if [ "${rev}" -eq 1 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          589825|589826)
+            new_fw_config=8670466
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    elemi)
+      if [ "${rev}" -le 2 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          720897|720898|720900|720901|720912|720913|720914|720916|\
+	  720928|720931|720933)
+            new_fw_config=4474114
+            ;;
+          720915|720929)
+            new_fw_config=4457730
+            ;;
+          720917|720918|720919|720935)
+            new_fw_config=8652034
+            ;;
+          720899|720902|720930|720932|720934)
+            new_fw_config=8668418
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    lillipup)
+      if [ "${rev}" -eq 1 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          524289)
+            new_fw_config=75845125
+            ;;
+          524290)
+            new_fw_config=75828741
+            ;;
+          524291)
+            new_fw_config=71634437
+            ;;
+          524292)
+            new_fw_config=71650821
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    lindar)
+      if [ "${rev}" -eq 1 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          458753)
+            new_fw_config=8717829
+            ;;
+          458754|458755)
+            new_fw_config=8734213
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    voema)
+      if [ "${rev}" -eq 1 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          851969|851970|851985|851986|851987)
+            new_fw_config=8408066
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    volta)
+      if [ "${rev}" -le 2 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          393233|393235|393237|393239|393241|393243|393245|393247)
+            new_fw_config=8602115
+            ;;
+          393234|393236|393238|393240|393242|393244|393246|393248)
+            new_fw_config=8585731
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    volteer)
+      if [ "${rev}" -eq 1 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          1)
+            new_fw_config=16861442
+            ;;
+          2)
+            new_fw_config=8472833
+            ;;
+          3|5)
+            new_fw_config=8542979
+            ;;
+          4)
+            new_fw_config=8542721
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    volteer2)
+      if [ "${rev}" -eq 2 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          6|10)
+            new_fw_config=16861442
+            ;;
+          7)
+            new_fw_config=8473089
+            ;;
+          8)
+            new_fw_config=8473091
+            ;;
+          9|11)
+            new_fw_config=8477443
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    voxel)
+      if [ "${rev}" -le 2 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          39321[7-9]|39322[0-8])
+            new_fw_config=8604163
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    *)
+      exit 0
+      ;;
+  esac
+
+  # if the fw_config is the same as new_fw_config or
+  # the new_fw_config is zero , skip it
+  if [ "${fw_config}" -eq "${new_fw_config}" -o \
+       "${new_fw_config}" -eq 0 ]; then
+    exit 0
+  fi
+
+  # apply the fw_config
+  ret=$(ssh ${target} ectool cbi set 6 "${new_fw_config}" 4)
+  if [ -n "${ret}" ]; then
+    logit "Failed to override fw_config to ${new_fw_config} for sku ${sku}"
+    exit 0
+  fi
+
+  logit "Override fw_config to ${new_fw_config} for sku ${sku}"
+}
+
+check_and_modify_fw_config "$@"
diff --git a/patching/b183567529/volteer-cbi-override.conf b/patching/b183567529/volteer-cbi-override.conf
new file mode 100644
index 0000000..2d59d25
--- /dev/null
+++ b/patching/b183567529/volteer-cbi-override.conf
@@ -0,0 +1,310 @@
+# Copyright 2021 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.
+
+description   "Override the cbi content for the devices"
+author        "chromiumos-dev@chromium.org"
+
+start on started system-services
+task
+oom score -100   # Short task at startup; don't oom kill
+
+script
+  set -e
+
+  logit() {
+    logger -t "${UPSTART_JOB}" "$@"
+  }
+
+  model="$(cros_config / name)"
+  rev="$(ectool cbi get 0 | grep 'As uint:' | cut -d' ' -f3)"
+  sku="$(ectool cbi get 2 | grep 'As uint:' | cut -d' ' -f3)"
+  fw_config="$(ectool cbi get 6 | grep 'As uint:' | cut -d' ' -f3)"
+  fw_type="$(crossystem mainfw_type)"
+  wpsw_cur="$(crossystem wpsw_cur)"
+  new_fw_config=0
+
+  # only apply to the normal mode devices and which fw_config is zero
+  # except copano and drobit
+  if [ "${fw_type}" != "normal" ] || \
+     [ "${fw_config}" -ne 0 -a \
+       "${model}" != "copano" -a \
+       "${model}" != "drobit" ]; then
+    exit 0
+  fi
+
+  # only apply to the board revision <= 2
+  if [ "${rev}" -gt 2 ]; then
+    exit 0
+  fi
+
+  # apply to copano, delbin, drobit
+  # eldrid, elemi, lillipup, lindar,
+  # voema, volteer2 and voxel
+  case ${model} in
+    copano)
+      if [ "${rev}" -eq 1 ]; then
+        case ${sku} in
+          917505)
+            new_fw_config=8604675
+            ;;
+          917506)
+            new_fw_config=42159107
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    delbin)
+      if [ "${rev}" -eq 2 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          65540)
+            new_fw_config=8456706
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    delbin_npcx796fc)
+      if [ "${rev}" -eq 1 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          65537|65538|65539)
+            new_fw_config=68098
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    drobit)
+      if [ "${rev}" -eq 1 ]; then
+        case ${sku} in
+          786433|786434|786435)
+            new_fw_config=8471043
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    eldrid)
+      if [ "${rev}" -eq 2 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          589841)
+            new_fw_config=8670466
+            ;;
+          589842)
+            new_fw_config=9719042
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    eldrid_npcx796)
+      if [ "${rev}" -eq 1 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          589825|589826)
+            new_fw_config=8670466
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    elemi)
+      if [ "${rev}" -le 2 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          720897|720898|720900|720901|720912|720913|720914|720916|\
+	  720928|720931|720933)
+            new_fw_config=4474114
+            ;;
+          720915|720929)
+            new_fw_config=4457730
+            ;;
+          720917|720918|720919|720935)
+            new_fw_config=8652034
+            ;;
+          720899|720902|720930|720932|720934)
+            new_fw_config=8668418
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    lillipup)
+      if [ "${rev}" -eq 1 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          524289)
+            new_fw_config=75845125
+            ;;
+          524290)
+            new_fw_config=75828741
+            ;;
+          524291)
+            new_fw_config=71634437
+            ;;
+          524292)
+            new_fw_config=71650821
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    lindar)
+      if [ "${rev}" -eq 1 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          458753)
+            new_fw_config=8717829
+            ;;
+          458754|458755)
+            new_fw_config=8734213
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    voema)
+      if [ "${rev}" -eq 1 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          851969|851970|851985|851986|851987)
+            new_fw_config=8408066
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    volta)
+      if [ "${rev}" -le 2 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          393233|393235|393237|393239|393241|393243|393245|393247)
+            new_fw_config=8602115
+            ;;
+          393234|393236|393238|393240|393242|393244|393246|393248)
+            new_fw_config=8585731
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    volteer)
+      if [ "${rev}" -eq 1 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          1)
+            new_fw_config=16861442
+            ;;
+          2)
+            new_fw_config=8472833
+            ;;
+          3|5)
+            new_fw_config=8542979
+            ;;
+          4)
+            new_fw_config=8542721
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    volteer2)
+      if [ "${rev}" -eq 2 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          6|10)
+            new_fw_config=16861442
+            ;;
+          7)
+            new_fw_config=8473089
+            ;;
+          8)
+            new_fw_config=8473091
+            ;;
+          9|11)
+            new_fw_config=8477443
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    voxel)
+      if [ "${rev}" -le 2 -a "${wpsw_cur}" -eq 0 ]; then
+        case ${sku} in
+          39321[7-9]|39322[0-8])
+            new_fw_config=8604163
+            ;;
+          *)
+            exit 0
+            ;;
+        esac
+      else
+        exit 0
+      fi
+      ;;
+    *)
+      exit 0
+      ;;
+  esac
+
+  # if the fw_config is the same as new_fw_config or
+  # the new_fw_config is zero , skip it
+  if [ "${fw_config}" -eq "${new_fw_config}" -o \
+       "${new_fw_config}" -eq 0 ]; then
+    exit 0
+  fi
+
+  # apply the fw_config
+  ret=$(ectool cbi set 6 "${new_fw_config}" 4)
+  if [ -n "${ret}" ]; then
+    logit "Failed to override fw_config to ${new_fw_config} for sku ${sku}"
+    exit 0
+  fi
+
+  logit "Override fw_config to ${new_fw_config} for sku ${sku}"
+end script