blob: acec3ec856b7ff8753e4d535ae201e16cc2ed6e5 [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package labqual
import (
"context"
"fmt"
"io"
"os"
"regexp"
"strings"
"time"
"github.com/google/uuid"
"go.chromium.org/tast-tests/cros/common/firmware/futility"
"go.chromium.org/tast-tests/cros/common/servo"
"go.chromium.org/tast-tests/cros/remote/dutfs"
"go.chromium.org/tast-tests/cros/remote/firmware"
"go.chromium.org/tast-tests/cros/remote/firmware/fixture"
"go.chromium.org/tast-tests/cros/remote/firmware/reporters"
"go.chromium.org/tast/core/errors"
"go.chromium.org/tast/core/ssh/linuxssh"
"go.chromium.org/tast/core/testing"
)
const (
tmpFirmwareDir = "/var/tmp/tmp"
alternateTmpFwDir = "/tmp/tmp"
backupFirmwareFile = "backupfw.bin"
imageGCSBucket = "chromeos-image-archive"
defaultTarSuffix = ".tar.bz2"
defaultTarballName = "firmware_from_source" + defaultTarSuffix
)
func init() {
testing.AddTest(&testing.Test{
Func: UpdateDutFirmware,
Desc: "Update AP and EC firmware from Servo",
Contacts: []string{
"peep-fleet-infra-sw@google.com",
},
BugComponent: "b:1032353", // Chrome Operations > Fleet > Software > OS Fleet Automation
Attr: []string{"group:labqual_informational", "group:labqual_stable"},
SoftwareDeps: []string{"chrome"},
ServiceDeps: []string{"tast.cros.firmware.BiosService", "tast.cros.firmware.UtilsService", "dutfs.ServiceName"},
Fixture: fixture.NormalMode,
LacrosStatus: testing.LacrosVariantUnneeded,
Timeout: 90 * time.Minute, // 1hr30min.
})
}
// UpdateDutFirmware flashes the AP and EC firmware if firmware file is specified
// otherwise reads the current AP firmware and flashes it back on the DUT
// Firmware can be specified using the vars "firmware.firmwarePath" for a GCS firmware file path or
// "firmware.localFirmwarePath" for a local firmware file location
func UpdateDutFirmware(ctx context.Context, s *testing.State) {
h := s.FixtValue().(*fixture.Value).Helper
firmwarePathVal := string(firmware.FirmwarePath.Value())
localFirmwarePathVal := string(firmware.LocalFirmwarePath.Value())
if firmwarePathVal != "" && localFirmwarePathVal != "" {
s.Fatal("Only one of localFirmwarePath or firmwarePath can be specified")
}
if localFirmwarePathVal != "" {
_, err := os.Stat(localFirmwarePathVal)
if err != nil {
s.Fatal("Could not stat specified local firmware path: ", err)
}
s.Log("Path to local firmware file : ", localFirmwarePathVal)
}
if firmwarePathVal != "" {
// Adding default suffix and prefix to GCS firmware file path if needed.
downloadFilename := firmwarePathVal
if !strings.HasPrefix(firmwarePathVal, imageGCSBucket) {
downloadFilename = fmt.Sprintf("%s/%s", imageGCSBucket, firmwarePathVal)
}
if !strings.HasSuffix(firmwarePathVal, defaultTarSuffix) {
downloadFilename = fmt.Sprintf("%s/%s", downloadFilename, defaultTarballName)
}
firmwarePathVal = downloadFilename
s.Log("GCS path to download firmware files : ", firmwarePathVal)
}
if err := h.RequireServo(ctx); err != nil {
s.Fatal("Failed to init servo: ", err)
}
ecChip, err := h.Servo.GetString(ctx, servo.ECChip)
if err != nil {
s.Fatal("Failed to read DUT EC Chip: ", err)
}
s.Log("DUT EC Chip: ", ecChip)
if err := h.RequireConfig(ctx); err != nil {
s.Fatal("Failed to get config: ", err)
}
// Confirm the CCD is open.
hasCCD, err := h.Servo.HasCCD(ctx)
if err != nil {
s.Fatal("Failed while checking if servo has a CCD connection: ", err)
}
if hasCCD {
if val, err := h.Servo.GetString(ctx, servo.GSCCCDLevel); err != nil {
s.Fatal("Failed to get gsc_ccd_level: ", err)
} else if val != servo.Open {
s.Logf("CCD is not open, got %q. Attempting to unlock", val)
if err := h.Servo.SetString(ctx, servo.GSCTestlab, servo.Open); err != nil {
s.Fatal("Failed to unlock CCD: ", err)
}
}
}
s.Log("Disabling hardware write protect")
err = h.Servo.SetFWWPState(ctx, servo.FWWPStateOff)
if err != nil {
s.Fatal("Failed to disable hardware write protect: ", err)
}
s.Log("Disabling software write protect")
out, err := h.ServoProxy.OutputCommand(ctx, true, "futility", "flash", "--wp-disable", fmt.Sprintf("--servo_port=%d", h.ServoProxy.GetPort()))
if err != nil {
s.Fatalf("Software write protect disable failed: %q, Output: %s", err, string(out))
}
s.Logf("Disabling software write protect completed, command output: %s", string(out))
// Check that the DUT is booted after disabling write protect
if err := h.EnsureDUTBooted(ctx); err != nil {
s.Fatal("Failed to reconnect to DUT after unsuspending: ", err)
}
uuid, _ := uuid.NewRandom()
s.Log("Creating tmp directories on dut, servo and testing host")
tmpDir, err := os.MkdirTemp("", "firmware-UpdateDUTFirmwareServo")
if err != nil {
s.Fatal("Failed to create a new directory for the test: ", err)
}
defer os.RemoveAll(tmpDir)
h.CloseRPCConnection(ctx)
if err := h.RequireRPCClient(ctx); err != nil {
s.Fatal("Failed to connect to the RPC service on the DUT: ", err)
}
dutTmpDir := fmt.Sprintf("%s-%s", tmpFirmwareDir, uuid)
servoTmpDir := dutTmpDir
// check whether the user account running the test has write permission for the tmp dir,
// if no then check the write permission for the alternate tmp dir. If both don't have permission fail the test.
if err := h.ServoProxy.RunCommand(ctx, false, "mkdir", "-p", servoTmpDir); err != nil {
s.Logf("Failed to create temp directory %s on servo for saving existing firmware: %s", servoTmpDir, err)
servoTmpDir = fmt.Sprintf("%s-%s", alternateTmpFwDir, uuid)
if err := h.ServoProxy.RunCommand(ctx, false, "mkdir", "-p", servoTmpDir); err != nil {
s.Fatalf("Failed to create aletrnate temp directory %s on servo for saving existing firmware: %s", servoTmpDir, err)
}
}
s.Logf("Servo tmp dir: %s", servoTmpDir)
// Delete the tmp directory on the servo at the end
defer func() {
s.Log("Deleting tmp directory on servo: ", servoTmpDir)
if err := h.ServoProxy.RunCommand(ctx, true, "rm", "-rf", servoTmpDir); err != nil {
s.Fatal("Failed to delete temp directory on servo for saving existing firmware: ", err)
}
}()
fs := dutfs.NewClient(h.RPCClient.Conn)
if err := fs.MkDir(ctx, dutTmpDir, 0644); err != nil {
s.Fatalf("Failed to create temp directory on dut %s for saving existing firmware: %s", dutTmpDir, err)
}
s.Log("DUT tmp Directory: ", dutTmpDir)
defer func() {
s.Log("Deleting tmp directory on dut: ", dutTmpDir)
h.CloseRPCConnection(ctx)
if err := h.RequireRPCClient(ctx); err != nil {
s.Fatal("Failed to connect to the RPC service on the DUT: ", err)
}
if err := dutfs.NewClient(h.RPCClient.Conn).RemoveAll(ctx, dutTmpDir); err != nil {
s.Fatal("Failed to delete temp directory on dut for saving existing firmware: ", err)
}
}()
// Get the initial fwid from 'crossystem fwid'.
initialRwFwid, err := h.Reporter.CrossystemParam(ctx, reporters.CrossystemParamFwid)
if err != nil {
s.Fatal("Failed to get crossystem fwid: ", err)
}
re := regexp.MustCompile(`Google_([a-z-A-Z_]*)\.(\d*\.\d*.\d*)`)
match := re.FindStringSubmatch(initialRwFwid)
if len(match) != 3 {
s.Fatalf("Unexpected fw id format from crossystem %v, got: %s", reporters.CrossystemParamFwid, initialRwFwid)
}
fwidModel := strings.ToLower(match[1])
initialRwFwid = match[2]
s.Logf("FWID Model : %s", fwidModel)
fwTargets, err := firmware.ReadFirmwareTargets(ctx, s.DUT().Conn(), h.Model, fwidModel)
s.Logf("Found AP Target: %s and EC Target: %s", fwTargets.APTarget, fwTargets.ECTarget)
// Get the RO firmware version ID available on the DUT.
initialROFwid, err := h.Reporter.GetFWVersion(ctx, reporters.CrossystemParamRoFwid)
if err != nil {
s.Fatal("Failed to get AP RO ID: ", err)
}
var ecBinToFlash, monitorBinToFlash, apBinToFlash string
if firmwarePathVal != "" || localFirmwarePathVal != "" {
if firmwarePathVal != "" {
s.Log("Downloading Firmware to Flash")
firmwareFilesToFlash, err := firmware.DownloadFirmwareFiles(ctx, s.CloudStorage(), h, servoTmpDir, firmwarePathVal, "", fwTargets)
if err != nil {
s.Fatal("Error while downloading firmware files: ", err)
}
apBinToFlash = firmwareFilesToFlash.APFirmwareFile
ecBinToFlash = firmwareFilesToFlash.ECFirmwareFile
if apBinToFlash == "" || ecBinToFlash == "" {
s.Fatalf("Failed to download required firmware files; APBinToFlash: %s; ECBinToFlash: %s ", apBinToFlash, ecBinToFlash)
}
// copy firmware files to the local host as they need to be copied to the DUT for DUT firmware flashing test
if err := h.ServoProxy.GetFile(ctx, false, fmt.Sprintf("%s/%s", servoTmpDir, firmware.APFirmwareFileToFlash), fmt.Sprintf("%s/%s", tmpDir, apBinToFlash)); err != nil {
s.Fatal("Failed to copy AP firmware file from servo host: ", err)
}
if err := h.ServoProxy.GetFile(ctx, false, fmt.Sprintf("%s/%s", servoTmpDir, firmware.ECFirmwareFileToFlash), fmt.Sprintf("%s/%s", tmpDir, ecBinToFlash)); err != nil {
s.Fatal("Failed to copy EC firmware file from servo host: ", err)
}
if firmwareFilesToFlash.MonitorFile != "" {
monitorBinToFlash = firmwareFilesToFlash.MonitorFile
if err := h.ServoProxy.GetFile(ctx, false, fmt.Sprintf("%s/%s", servoTmpDir, firmware.MonitorFileToFlash), fmt.Sprintf("%s/%s", tmpDir, monitorBinToFlash)); err != nil {
s.Fatal("Failed to copy EC monitor firmware file from servo host: ", err)
}
}
} else {
ecBinToFlash, monitorBinToFlash, apBinToFlash = untarLocalFirmwareFile(ctx, s, tmpDir, localFirmwarePathVal, fwidModel)
// copy firmware files to the labstation as they are needed for servo firmware flashing test
fileMap := map[string]string{
fmt.Sprintf("%s/%s", tmpDir, apBinToFlash): fmt.Sprintf("%s/%s", servoTmpDir, firmware.APFirmwareFileToFlash),
}
if ecBinToFlash != "" {
fileMap[fmt.Sprintf("%s/%s", tmpDir, ecBinToFlash)] = fmt.Sprintf("%s/%s", servoTmpDir, firmware.ECFirmwareFileToFlash)
}
if monitorBinToFlash != "" {
fileMap[fmt.Sprintf("%s/%s", tmpDir, monitorBinToFlash)] = fmt.Sprintf("%s/%s", servoTmpDir, firmware.MonitorFileToFlash)
}
if err := h.ServoProxy.PutFiles(ctx, false, fileMap); err != nil {
s.Fatal("Failed to copy files to servo host: ", err)
}
}
s.Logf("EC Firmware to Flash %s; monitor file to flash %s; AP Firmware to Flash %s", ecBinToFlash, monitorBinToFlash, apBinToFlash)
dutFileMap := map[string]string{}
if ecBinToFlash != "" {
dutFileMap[fmt.Sprintf("%s/%s", tmpDir, ecBinToFlash)] = fmt.Sprintf("%s/%s", dutTmpDir, firmware.ECFirmwareFileToFlash)
}
if monitorBinToFlash != "" {
dutFileMap[fmt.Sprintf("%s/%s", tmpDir, monitorBinToFlash)] = fmt.Sprintf("%s/%s", dutTmpDir, firmware.MonitorFileToFlash)
}
s.Log("Copying EC firmware files to dut")
if _, err := linuxssh.PutFiles(ctx, s.DUT().Conn(), dutFileMap, linuxssh.PreserveSymlinks); err != nil {
s.Fatal("Failed to copy files to dut: ", err)
}
s.Logf("Files under %s: before FW flashing", dutTmpDir)
statFirmwareFilesOnDUT(ctx, s, h, dutTmpDir)
flashECFirmwareFromDut(ctx, s, h, dutTmpDir, tmpDir, ecBinToFlash, monitorBinToFlash, ecChip)
flashECFirmware(ctx, s, h, servoTmpDir, tmpDir, ecChip)
}
// AP firmware file is copied to the DUT after EC flashing to handle a corner case for some models
// where all the firmware files in dut tmp directory become empty after EC firmware flashing.
dutFileMap := map[string]string{
fmt.Sprintf("%s/%s", tmpDir, apBinToFlash): fmt.Sprintf("%s/%s", dutTmpDir, firmware.APFirmwareFileToFlash),
}
s.Log("Copying AP firmware file to dut")
if _, err := linuxssh.PutFiles(ctx, s.DUT().Conn(), dutFileMap, linuxssh.PreserveSymlinks); err != nil {
s.Fatal("Failed to copy files to dut: ", err)
}
s.Logf("Files under %s: before AP flashing", dutTmpDir)
statFirmwareFilesOnDUT(ctx, s, h, dutTmpDir)
flashAPFirmwareFromDut(ctx, s, h, dutTmpDir, tmpDir, firmwarePathVal, localFirmwarePathVal, initialROFwid, initialRwFwid)
flashAPFirmware(ctx, s, h, servoTmpDir, firmwarePathVal, localFirmwarePathVal, ecChip, initialROFwid, initialRwFwid)
}
// untarLocalFirmwareFile untars the provided local firmware file to extract AP and EC images
func untarLocalFirmwareFile(ctx context.Context, s *testing.State, tmpDir, firmwareFilepath, model string) (ecBinToFlash, monitorBinToFlash, apBinToFlash string) {
// Copy the fw file to tmp directory.
dst, err := os.Create(tmpDir + "/" + firmware.FirmwareFileName)
s.Logf("Firmware File Path: %s; Tmp File Path: %s", firmwareFilepath, tmpDir)
if err != nil {
s.Fatalf("Failed to open tmp file %q: %s", tmpDir+"/"+firmware.FirmwareFileName, err)
}
// Close file on exit
defer func() error {
if err := dst.Close(); err != nil {
s.Fatalf("Failed to close tmp file %q: %s", tmpDir+"/"+firmware.FirmwareFileName, err)
}
return nil
}()
src, err := os.Open(firmwareFilepath)
if err != nil {
s.Fatalf("Failed to open local firmware file %s for copying to tmp directory: %s", firmwareFilepath, err)
}
defer src.Close()
if _, err := io.Copy(dst, src); err != nil {
s.Fatalf("Failed to copy firmware file to tmp location: %s", err)
}
// Untar the binary file with respect to the model name.
apBinToFlash, _, err = firmware.UntarUnknownFileName(ctx, tmpDir, model, firmware.APFirmware)
if err != nil {
s.Fatalf("Failed to untar file for %s: %s", firmware.APFirmware, err)
}
ecBinToFlash, monitorBinToFlash, err = firmware.UntarUnknownFileName(ctx, tmpDir, model, firmware.ECFirmware)
if err != nil {
s.Fatalf("Failed to untar file for %s: %s", firmware.ECFirmware, err)
}
return ecBinToFlash, monitorBinToFlash, apBinToFlash
}
// flashECFirmware flashes the provided EC firmware on the DUT and restores the original EC firmware in the end.
func flashECFirmware(ctx context.Context, s *testing.State, h *firmware.Helper, servoTmpDir, localTmpDir, ecChip string) {
backupECFirmware(ctx, s, h, servoTmpDir, ecChip)
// Check that the DUT has initial fw in the end
defer func() {
h.DisconnectDUT(ctx)
runECFirmwareFlashServo(ctx, s, h, servoTmpDir, ecChip, backupFirmwareFile)
}()
// Flash EC
s.Log("Flashing DUT EC with downloaded firmware file")
runECFirmwareFlashServo(ctx, s, h, servoTmpDir, ecChip, firmware.ECFirmwareFileToFlash)
s.Log("Completed flashing of downloaded ec fw")
}
// flashAPFirmware flashes the provided AP firmware on the DUT and restores the original AP firmware in the end.
func flashAPFirmware(ctx context.Context, s *testing.State, h *firmware.Helper, servoTmpDir, firmwarePathVal, localFirmwarePathVal, ecChip, initialROFwid, initialRwFwid string) {
futilityInstance, err := futility.NewRemoteBuilder(h.ServoProxy).Build()
s.Log("Backing up AP firmware")
backupFirmwareFile := fmt.Sprintf("%s/%s", servoTmpDir, backupFirmwareFile)
readOpts := futility.NewReadAPOptions(backupFirmwareFile)
log, err := futilityInstance.ReadAP(ctx, readOpts)
if err != nil {
s.Fatalf("Failed to read existing AP firmware: %v, got futility log: %s", err, string(log))
}
s.Logf("Completed backup of existing AP fw, command output: %s", log)
if err := h.EnsureDUTBooted(ctx); err != nil {
s.Fatal("Failed to reconnect to DUT after unsuspending: ", err)
}
// Check that the DUT has initial fw in the end
defer func() {
s.Log("Flashing DUT with backup AP firmware file")
flashOpts := futility.NewUpdateOptions(backupFirmwareFile).
WithMode(futility.UpdateModeRecovery).
WithGBBFlags(24)
out, err := futilityInstance.Update(ctx, flashOpts)
if err != nil {
s.Logf("Failed to flash firmware bin file: %s, Output:%s", err, string(out))
} else {
s.Logf("Completed flashing of backup AP fw, command output: %s", string(out))
}
if err := safeRebootDut(ctx, h); err != nil {
s.Fatal("Failed to reboot DUT after flashing: ", err)
}
// Verify RO/RW firmware versions are the prior ones after flashing.
// This is when RO and RW have the same version ids (i.e., RO_old + RW_old).
if err := firmware.VerifyFwIDs(ctx, h, initialROFwid, initialRwFwid); err != nil {
s.Log("Failed while verifying firmware IDs after flashing backup fw at the end of test: ", err)
}
}()
if firmwarePathVal == "" && localFirmwarePathVal == "" {
return
}
s.Log("Flashing DUT AP with downloaded firmware file")
apFirmwareFile := fmt.Sprintf("%s/%s", servoTmpDir, firmware.APFirmwareFileToFlash)
flashOpts := futility.NewUpdateOptions(apFirmwareFile).
WithMode(futility.UpdateModeRecovery).
WithGBBFlags(24)
out, err := futilityInstance.Update(ctx, flashOpts)
if err != nil {
s.Fatal("Failed to flash firmware bin file: ", err, "\nOutput:\n", string(out))
}
s.Logf("Completed flashing of downloaded fw, command output: %s", string(out))
if err := safeRebootDut(ctx, h); err != nil {
s.Fatal("Failed to reboot DUT after flashing: ", err)
}
// To verify firmware versions we need the filename to be in a certain format which
// locally downloaded firmware file may not be in
// so only verying versions for firmware downloaded from GCS
if firmwarePathVal == "" {
return
}
// Verify RO/RW firmware versions are the downloaded firmware versions after flashing.
// This is when RO and RW have the same version ids (i.e., RO_old + RW_old).
if err := firmware.VerifyFwIDs(ctx, h, firmwarePathVal, firmwarePathVal); err != nil {
s.Fatalf("After flashing RO_old + RW_old ( %s + %s ): %v", firmwarePathVal, firmwarePathVal, err)
}
}
// flashAPFirmwareFromDut flashes the provided AP firmware on the DUT and restores the original AP firmware in the end.
func flashAPFirmwareFromDut(ctx context.Context, s *testing.State, h *firmware.Helper, dutTmpDir, localTmpDir, firmwarePathVal, localFirmwarePathVal, initialROFwid, initialRwFwid string) {
futilityInstance, err := futility.NewLocalBuilder(h.DUT).Build()
s.Log("Backing up AP firmware")
readOpts := futility.NewReadAPOptions(fmt.Sprintf("%s/%s", dutTmpDir, backupFirmwareFile))
log, err := futilityInstance.ReadAP(ctx, readOpts)
if err != nil {
s.Fatalf("Failed to read existing AP firmware: %v, got futility log: %s", err, string(log))
}
s.Logf("Completed backup of existing AP fw, command output: %s", log)
if err := h.EnsureDUTBooted(ctx); err != nil {
s.Fatal("Failed to reconnect to DUT after unsuspending: ", err)
}
// Copy backup file from dut to host
err = linuxssh.GetFile(ctx, s.DUT().Conn(), fmt.Sprintf("%s/%s", dutTmpDir, backupFirmwareFile), fmt.Sprintf("%s/%s", localTmpDir, backupFirmwareFile), linuxssh.PreserveSymlinks)
if err != nil {
s.Fatal("Failed to copy file from DUT to Host: ", err)
}
s.Log("Completed backup of existing AP fw")
// Restore the initial fw to the DUT in the end
defer func() {
s.Log("Flashing DUT with backup AP firmware file")
if _, err := linuxssh.PutFiles(ctx, s.DUT().Conn(),
map[string]string{fmt.Sprintf("%s/%s", localTmpDir, backupFirmwareFile): fmt.Sprintf("%s/%s", dutTmpDir, backupFirmwareFile)},
linuxssh.PreserveSymlinks); err != nil {
s.Fatal("Failed to copy files to dut: ", err)
}
if err := h.DUT.Conn().CommandContext(ctx, "chromeos-firmwareupdate", "-i", fmt.Sprintf("%s/%s", dutTmpDir, backupFirmwareFile)).Run(); err != nil {
s.Log("Failed to flash backup firmware bin file: ", err)
} else {
s.Log("Completed flashing of backup AP fw")
}
if err := safeRebootDut(ctx, h); err != nil {
s.Fatal("Failed to reboot DUT after flashing: ", err)
}
// Verify RO/RW firmware versions are the prior ones after flashing.
// This is when RO and RW have the same version ids (i.e., RO_old + RW_old).
if err := firmware.VerifyFwIDs(ctx, h, initialROFwid, initialRwFwid); err != nil {
s.Log("Failed while verifying firmware IDs after flashing backup fw at the end of test: ", err)
}
}()
if firmwarePathVal == "" && localFirmwarePathVal == "" {
return
}
s.Log("Flashing DUT AP with downloaded firmware file")
if err := h.DUT.Conn().CommandContext(ctx, "chromeos-firmwareupdate", "-i", fmt.Sprintf("%s/%s", dutTmpDir, firmware.APFirmwareFileToFlash)).Run(); err != nil {
s.Fatal("Failed to flash firmware bin file: ", err)
}
s.Log("Completed flashing of downloaded fw")
if err := safeRebootDut(ctx, h); err != nil {
s.Fatal("Failed to reboot DUT after flashing: ", err)
}
// To verify firmware versions we need the filename to be in a certain format which
// locally downloaded firmware file may not be in
// so only verying versions for firmware downloaded from GCS
if firmwarePathVal == "" {
return
}
// Verify RO/RW firmware versions are the downloaded firmware versions after flashing.
// This is when RO and RW have the same version ids (i.e., RO_old + RW_old).
if err := firmware.VerifyFwIDs(ctx, h, firmwarePathVal, firmwarePathVal); err != nil {
s.Fatalf("After flashing RO_old + RW_old ( %s + %s ): %v", firmwarePathVal, firmwarePathVal, err)
}
}
// flashECFirmwareFromDut flashes the provided EC firmware on the DUT and restores the original EC firmware in the end.
func flashECFirmwareFromDut(ctx context.Context, s *testing.State, h *firmware.Helper, tmpFwDir, localTmpDir, ecBinToFlash, monitorBinToFlash, ecChip string) {
s.Log("Backing up EC firmware")
backupECFirmware(ctx, s, h, tmpFwDir, ecChip)
s.Log("Completed backup of existing EC fw")
// Check that the DUT has initial fw in the end
defer func() {
// Copy backup file from servo to host
err := h.ServoProxy.GetFile(ctx, false, fmt.Sprintf("%s/%s", tmpFwDir, backupFirmwareFile), fmt.Sprintf("%s/%s", localTmpDir, backupFirmwareFile))
if err != nil {
s.Fatal("Failed to copy file from Servo to Host: ", err)
}
s.Log("Flashing DUT with backup EC firmware file")
if err := h.EnsureDUTBooted(ctx); err != nil {
s.Fatal("Failed to reconnect to DUT after unsuspending: ", err)
}
if _, err := linuxssh.PutFiles(ctx, h.DUT.Conn(), map[string]string{fmt.Sprintf("%s/%s", localTmpDir, backupFirmwareFile): fmt.Sprintf("%s/%s", tmpFwDir, backupFirmwareFile)}, linuxssh.PreserveSymlinks); err != nil {
s.Fatal("Failed to copy files to dut: ", err)
}
runECFirmwareFlashDut(ctx, s, h, tmpFwDir, backupFirmwareFile, true)
s.Log("Completed flashing of backup EC fw")
}()
s.Log("Flashing DUT EC with downloaded firmware file")
if _, err := linuxssh.PutFiles(ctx, h.DUT.Conn(), map[string]string{fmt.Sprintf("%s/%s", localTmpDir, ecBinToFlash): fmt.Sprintf("%s/%s", tmpFwDir, firmware.ECFirmwareFileToFlash)}, linuxssh.PreserveSymlinks); err != nil {
s.Fatal("Failed to copy files to dut: ", err)
}
runECFirmwareFlashDut(ctx, s, h, tmpFwDir, firmware.ECFirmwareFileToFlash, false)
s.Log("Completed flashing of downloaded fw")
if err := h.EnsureDUTBooted(ctx); err != nil {
s.Fatal("Failed to reconnect to DUT after unsuspending: ", err)
}
}
// safeRebootDut will close RPC connection, reboot DUT and Open a new RPC connection.
func safeRebootDut(ctx context.Context, h *firmware.Helper) error {
// Close RPC connection before reboot.
h.CloseRPCConnection(ctx)
testing.ContextLog(ctx, "Power-cycling DUT with a cold reset")
if err := h.Servo.SetPowerState(ctx, servo.PowerStateReset); err != nil {
return errors.Wrap(err, "failed to reboot DUT by servo")
}
testing.ContextLog(ctx, "Waiting for DUT to reconnect")
connectCtx, cancelconnectCtx := context.WithTimeout(ctx, 10*time.Minute)
defer cancelconnectCtx()
if err := h.WaitConnect(connectCtx); err != nil {
return errors.Wrap(err, "failed to reconnect to DUT")
}
// Open RPC connection after reboot.
if err := h.RequireRPCClient(ctx); err != nil {
return errors.Wrap(err, "failed to open RPC client after reboot")
}
if err := h.EnsureDUTBooted(ctx); err != nil {
return errors.Wrap(err, "failed to reconnect to DUT after reboot")
}
return nil
}
// backupECFirmware takes a backup of current EC firmware.
func backupECFirmware(ctx context.Context, s *testing.State, h *firmware.Helper, servoTmpDir, ecChip string) {
h.DisconnectDUT(ctx)
flashCmd := fmt.Sprintf("cd %s&&flash_ec --port=%d --read=%s/%s", servoTmpDir, h.ServoProxy.GetPort(), servoTmpDir, backupFirmwareFile)
if strings.HasPrefix(ecChip, "it8") {
flashCmd += " --nouse_i2c_pseudo"
}
if err := h.ServoProxy.RunCommand(ctx, true, "bash", "-c", flashCmd); err != nil {
s.Fatal("Failed to backup EC firmware: ", err)
}
if err := safeRebootDut(ctx, h); err != nil {
s.Fatal("Failed to reboot DUT after backup EC: ", err)
}
}
// runECFirmwareFlashServo runs EC firmware flashing from the Servo
func runECFirmwareFlashServo(ctx context.Context, s *testing.State, h *firmware.Helper, servoTmpDir, ecChip, image string) {
flashECArgs := []string{fmt.Sprintf("--chip=%s", ecChip), fmt.Sprintf("--image=%s/%s", servoTmpDir, image), fmt.Sprintf("--port=%d", h.ServoProxy.GetPort()), "--verify", "--verbose"}
if ecChip == "stm32" {
flashECArgs = append(flashECArgs, "--bitbang_rate=57600")
}
if strings.HasPrefix(ecChip, "it8") {
flashECArgs = append(flashECArgs, "--nouse_i2c_pseudo")
}
if err := h.ServoProxy.RunCommand(ctx, true, "flash_ec", flashECArgs...); err != nil {
s.Fatal("Failed to flash EC firmware bin file: ", err)
}
if err := h.EnsureDUTBooted(ctx); err != nil {
s.Fatal("Can't restore firmware, DUT is off: ", err)
}
}
// runECFirmwareFlashDut runs EC firmware flashing from the DUT
func runECFirmwareFlashDut(ctx context.Context, s *testing.State, h *firmware.Helper, dutTmpDir, image string, allowFlashFailure bool) {
if err := h.DUT.Conn().CommandContext(ctx, "chromeos-firmwareupdate", "--ec_image", fmt.Sprintf("%s/%s", dutTmpDir, image)).Run(); err != nil {
if !allowFlashFailure {
s.Fatal("Failed to flash firmware bin file: ", err)
}
s.Log("Failed to flash firmware bin file: ", err)
}
if err := safeRebootDut(ctx, h); err != nil {
s.Fatal("Failed to reboot DUT after flashing: ", err)
}
}
// statFirmwareFilesOnDUT stats the files in the temporary fw dir on the DUT
func statFirmwareFilesOnDUT(ctx context.Context, s *testing.State, h *firmware.Helper, tmpFwDir string) {
h.CloseRPCConnection(ctx)
if err := h.RequireRPCClient(ctx); err != nil {
s.Fatal("Failed to connect to the RPC service on the DUT: ", err)
}
fs := dutfs.NewClient(h.RPCClient.Conn)
fis, err := fs.ReadDir(ctx, tmpFwDir)
if err != nil {
s.Fatalf("Failed to list files at %s: %v", tmpFwDir, err)
}
for _, fi := range fis {
s.Logf("Firmware file Name: %s; file size: %d", fi.Name(), fi.Size())
}
}