blob: ebe0a21839f4359715ccc9c7046cd4011d024ffc [file] [log] [blame]
// 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.
package firmware
import (
"context"
"path/filepath"
"regexp"
"time"
"chromiumos/tast/common/firmware/ti50"
"chromiumos/tast/errors"
"chromiumos/tast/remote/dutfs"
"chromiumos/tast/remote/firmware/ti50/fixture"
"chromiumos/tast/rpc"
"chromiumos/tast/ssh/linuxssh"
"chromiumos/tast/testing"
)
const ti50USBID = "18d1:504a"
var consoleUpdateTooSoonRegexp = regexp.MustCompile("Attempted update too soon")
var gsctoolUpdateTooSoonRegexp = regexp.MustCompile(`Error: status 0x9`)
var gsctoolUpdateSuccessRegexp = regexp.MustCompile(`image updated`)
var versionRwARegexp = regexp.MustCompile(`RW_A: \* [0-9.]+/ti50_common:v.*[\r\n]+RW_B: Empty`)
var versionRwBRegexp = regexp.MustCompile(`RW_B: \* [0-9.]+/ti50_common:v.*`)
func init() {
testing.AddTest(&testing.Test{
Func: Ti50CCDUpdate,
Desc: "Ti50 firmware update over CCD using gsctool",
Timeout: 5 * time.Minute,
Vars: []string{"serial"},
Contacts: []string{
"ecgh@chromium.org",
"ti50-core@google.com",
},
Attr: []string{"group:firmware"},
Fixture: fixture.Ti50,
})
}
// Ti50CCDUpdate requires HW setup with SuzyQ cable from Andreiboard to DUT.
func Ti50CCDUpdate(ctx context.Context, s *testing.State) {
serial := s.RequiredVar("serial")
lsusbSerialRegexp := regexp.MustCompile(`iSerial\s+\d\s` + serial)
f := s.FixtValue().(*fixture.Value)
board, err := f.DevBoard(ctx, 10000, time.Second)
if err != nil {
s.Fatal("Could not get board: ", err)
}
dutImage, err := prepareCcdImageFile(ctx, s, f.ImagePath)
if err != nil {
s.Fatal("Prepare file: ", err)
}
if err = board.Reset(ctx); err != nil {
s.Fatal("Failed to reset: ", err)
}
i := ti50.NewCrOSImage(board)
// Wait for reboot output to finish before reading version.
testing.Sleep(ctx, 1*time.Second)
outStr, err := i.Command(ctx, "version")
if err != nil {
s.Fatal("Console version: ", err)
}
testing.ContextLog(ctx, "Version before gsctool update: ")
testing.ContextLog(ctx, outStr)
// Before update should be running RW_A.
if !versionRwARegexp.MatchString(outStr) {
s.Fatal("Not running RW_A")
}
cmd := s.DUT().Conn().CommandContext(ctx, "lsusb", "-d", ti50USBID, "-v")
out, err := cmd.CombinedOutput()
outStr = string(out)
if err != nil {
s.Fatal("Failed lsusb: ", err, outStr)
}
if !lsusbSerialRegexp.MatchString(outStr) {
s.Fatal("Failed to match serial: ", outStr)
}
cmd = s.DUT().Conn().CommandContext(ctx, "/usr/sbin/gsctool", "-n", serial, "-f")
out, err = cmd.CombinedOutput()
if err != nil {
s.Fatalf("Failed to read version: %v: %s", err, out)
}
// Ti50 will reject updates for 60 seconds.
testing.Sleep(ctx, 30*time.Second)
cmd = s.DUT().Conn().CommandContext(ctx, "/usr/sbin/gsctool", "-n", serial, dutImage)
out, _ = cmd.CombinedOutput()
if !gsctoolUpdateTooSoonRegexp.Match(out) {
s.Fatalf("Wrong gsctool output for update too soon: %s", out)
}
_, err = board.ReadSerialSubmatch(ctx, consoleUpdateTooSoonRegexp)
if err != nil {
s.Fatal("Wrong console message for update too soon: ", err)
}
testing.Sleep(ctx, 30*time.Second)
cmd = s.DUT().Conn().CommandContext(ctx, "/usr/sbin/gsctool", "-n", serial, dutImage)
out, _ = cmd.CombinedOutput()
if !gsctoolUpdateSuccessRegexp.Match(out) {
s.Fatalf("Wrong gsctool output for update: %s", out)
}
// Wait for reboot output to finish before reading version.
testing.Sleep(ctx, 1*time.Second)
outStr, err = i.Command(ctx, "version")
if err != nil {
s.Fatal("Console version: ", err)
}
testing.ContextLog(ctx, "Version after gsctool update: ")
testing.ContextLog(ctx, outStr)
// After update should be running RW_B.
if !versionRwBRegexp.MatchString(outStr) {
s.Fatal("Not running RW_B")
}
}
// prepareCcdImageFile copies the Ti50 image file to the DUT and zeros the RO
// signature field. Returns the DUT file path to be used by gsctool to perform
// the CCD update.
func prepareCcdImageFile(ctx context.Context, s *testing.State, image string) (string, error) {
if image == "" {
return "", errors.New("no image file")
}
rpcClient, err := rpc.Dial(ctx, s.DUT(), s.RPCHint())
if err != nil {
return "", err
}
defer rpcClient.Close(ctx)
dutfsClient := dutfs.NewClient(rpcClient.Conn)
workDir, err := dutfsClient.TempDir(ctx, "", "")
if err != nil {
return "", err
}
dutImage := filepath.Join(workDir, "ti50.bin")
testing.ContextLogf(ctx, "Copy image %s to DUT %s", image, dutImage)
_, err = linuxssh.PutFiles(ctx, s.DUT().Conn(), map[string]string{image: dutImage}, linuxssh.DereferenceSymlinks)
if err != nil {
return "", err
}
// Zero the signature field (offsets 4-100) in RO_A and RO_B since we
// are using node locked RO on Andreiboard for testing (b/215718883).
cmd := s.DUT().Conn().CommandContext(ctx, "dd", "seek=4", "count=96", "bs=1", "conv=nocreat,notrunc", "if=/dev/zero", "of="+dutImage)
if err := cmd.Run(); err != nil {
return "", err
}
cmd = s.DUT().Conn().CommandContext(ctx, "dd", "seek=524292", "count=96", "bs=1", "conv=nocreat,notrunc", "if=/dev/zero", "of="+dutImage)
if err := cmd.Run(); err != nil {
return "", err
}
return dutImage, nil
}