blob: ae1ceae8c6d31ab860946c12ea2dafe4424c28eb [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"
"time"
"chromiumos/tast/ctxutil"
"chromiumos/tast/errors"
"chromiumos/tast/remote/dutfs"
"chromiumos/tast/remote/firmware/fingerprint"
"chromiumos/tast/remote/firmware/fingerprint/rpcdut"
"chromiumos/tast/testing"
"chromiumos/tast/testing/hwdep"
)
func init() {
testing.AddTest(&testing.Test{
Func: FpRDP0,
Desc: "Validate read protection (RDP) level 0 of the fingerprint firmware works as expected",
Contacts: []string{
"tomhughes@chromium.org",
"chromeos-fingerprint@google.com",
},
Attr: []string{"group:mainline", "informational"},
Timeout: 15 * time.Minute,
SoftwareDeps: []string{"biometrics_daemon"},
HardwareDeps: hwdep.D(hwdep.Fingerprint()),
ServiceDeps: []string{"tast.cros.platform.UpstartService", dutfs.ServiceName},
Vars: []string{"servo"},
})
}
func FpRDP0(ctx context.Context, s *testing.State) {
d, err := rpcdut.NewRPCDUT(ctx, s.DUT(), s.RPCHint(), "cros")
if err != nil {
s.Fatal("Failed to connect RPCDUT: ", err)
}
defer d.Close(ctx)
// Set both HW and SW write protect to false to get RDP0 state.
servoSpec, ok := s.Var("servo")
if !ok {
servoSpec = ""
}
t, err := fingerprint.NewFirmwareTest(ctx, d, servoSpec, s.OutDir(), false, false)
if err != nil {
s.Fatal("Failed to create new firmware test: ", err)
}
ctxForCleanup := ctx
defer func() {
if err := t.Close(ctxForCleanup); err != nil {
s.Fatal("Failed to clean up: ", err)
}
}()
ctx, cancel := ctxutil.Shorten(ctx, t.CleanupTime())
defer cancel()
// This test requires a forced flash without entropy
// initialization to ensure that we get an exact match between
// the original firmware that was flashed and the value that is
// read.
testing.ContextLog(ctx, "Force flashing original FP firmware")
if err := fingerprint.FlashFirmware(ctx, d, t.NeedsRebootAfterFlashing()); err != nil {
s.Fatal("Failed to flash original FP firmware: ", err)
}
// Wait for FPMCU to boot to RW. Fail if it does not.
testing.ContextLog(ctx, "Waiting for FPMCU to reboot to RW")
if err := fingerprint.WaitForRunningFirmwareImage(ctx, d.DUT(), fingerprint.ImageTypeRW); err != nil {
s.Fatal("Failed to boot to RW image: ", err)
}
// Rollback should be unset for this test.
testing.ContextLog(ctx, "Validating initial rollback state")
if err := fingerprint.CheckRollbackState(ctx, d, fingerprint.RollbackState{
BlockID: 0, MinVersion: 0, RWVersion: 0}); err != nil {
s.Fatal("Failed to validate rollback state: ", err)
}
testing.ContextLog(ctx, "Checking that firmware is functional")
if _, err := fingerprint.CheckFirmwareIsFunctional(ctx, d.DUT()); err != nil {
s.Fatal("Firmware is not functional after initialization: ", err)
}
// Given:
// * Hardware write protect is disabled
// * Software write protect is disabled
// * RDP is at level 0
//
// Then:
// * Reading from flash without changing the RDP level should succeed
// (we're already at level 0). Thus we should be able to read the
// entire firmware out of flash and it should exactly match the
// firmware that we flashed for testing.
testing.ContextLog(ctx, "Reading firmware without modifying RDP level")
if err := testRDP0(ctx, d, t.BuildFwFile(), false, t.NeedsRebootAfterFlashing()); err != nil {
s.Fatal("Failed to validate RDP0 without changing RDP level: ", err)
}
// Given:
// * Hardware write protect is disabled
// * Software write protect is disabled
// * RDP is at level 0
//
// Then:
// * Changing the RDP level to 0 should have no effect
// (we're already at level 0). Thus we should be able to read the
// entire firmware out of flash and it should exactly match the
// firmware that we flashed for testing.
testing.ContextLog(ctx, "Reading firmware while setting RDP to level 0")
if err := testRDP0(ctx, d, t.BuildFwFile(), true, t.NeedsRebootAfterFlashing()); err != nil {
s.Fatal("Failed to validate RDP0 while setting RDP to level 0: ", err)
}
}
// testRDP0 tests RDP0 functionality by trying to read from flash.
func testRDP0(ctx context.Context, d *rpcdut.RPCDUT, buildFwFile string, removeFlashReadProtect, needsReboot bool) (e error) {
var fileReadFromFlash string
var args []string
fs := dutfs.NewClient(d.RPC().Conn)
tempdirPath, err := fs.TempDir(ctx, "", "fingerprint_rdp0_*")
if err != nil {
return errors.Wrap(err, "failed to create remote temp directory")
}
defer func() {
tempDirExists, err := fs.Exists(ctx, tempdirPath)
if err != nil {
e = errors.Wrapf(err, "failed to check existence of temp directory: %q", tempdirPath)
return
}
if !tempDirExists {
// If we rebooted, the directory may no longer exist.
return
}
if err := fs.RemoveAll(ctx, tempdirPath); err != nil {
e = errors.Wrapf(err, "failed to remove temp directory: %q", tempdirPath)
}
}()
if removeFlashReadProtect {
// Use different file name to avoid errors in removing file.
fileReadFromFlash = filepath.Join(tempdirPath, "test_rdp0.bin")
args = []string{"--noservices", "--read", fileReadFromFlash}
} else {
fileReadFromFlash = filepath.Join(tempdirPath, "test_rdp0_noremove.bin")
args = []string{"--noservices", "--read",
"--noremove_flash_read_protect", fileReadFromFlash}
}
cmd := d.Conn().CommandContext(ctx, "flash_fp_mcu", args...)
out, err := cmd.CombinedOutput()
testing.ContextLog(ctx, "flash_fp_mcu output:", "\n", string(out))
if err != nil {
return errors.Wrap(err, "failed to read from flash")
}
testing.ContextLog(ctx, "Checking that value read matches the flashed version")
cmd = d.Conn().CommandContext(ctx, "cmp", buildFwFile, fileReadFromFlash)
if err := cmd.Run(); err != nil {
return errors.Wrap(err, "file read from flash does not match original fw file")
}
if needsReboot {
testing.ContextLog(ctx, "Rebooting")
if err := d.Reboot(ctx); err != nil {
return errors.Wrap(err, "failed to reboot DUT")
}
}
if _, err := fingerprint.CheckFirmwareIsFunctional(ctx, d.DUT()); err != nil {
return errors.Wrap(err, "firmware is not functional after reading flash")
}
return nil
}