blob: 1d7714f7485103323f29b1f21feb95e50939c8f3 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package hwsec
import (
"context"
"strconv"
"time"
"go.chromium.org/tast-tests/cros/common/servo"
"go.chromium.org/tast-tests/cros/remote/firmware/fixture"
"go.chromium.org/tast/core/errors"
"go.chromium.org/tast/core/testing"
"go.chromium.org/tast/core/testing/hwdep"
)
type cCDCapabilitiesOverrideWP struct {
capState servo.CCDCapState
wpCommandSucceedsWhenCCDOpen bool
wpCommandSucceedsWhenCCDLocked bool
}
func init() {
testing.AddTest(&testing.Test{
Func: CCDCapabilitiesOverrideWP,
Desc: "Test to verify OverrideWP CCD capability controls access to the `wp` console command",
Attr: []string{"group:firmware", "group:hwsec", "firmware_cr50"},
Contacts: []string{
"cros-hwsec@google.com",
"mvertescher@google.com",
"chromeos-faft@google.com",
},
BugComponent: "b:1188704",
Fixture: fixture.NormalMode,
HardwareDeps: hwdep.D(hwdep.GSCUART()),
SoftwareDeps: []string{"gsc", "reboot"},
Timeout: 2 * time.Minute,
Vars: []string{"servo"},
Params: []testing.Param{{
Name: "cap_default",
Val: cCDCapabilitiesOverrideWP{
capState: servo.CapDefault,
wpCommandSucceedsWhenCCDOpen: true,
wpCommandSucceedsWhenCCDLocked: false,
},
}, {
Name: "cap_always",
Val: cCDCapabilitiesOverrideWP{
capState: servo.CapAlways,
wpCommandSucceedsWhenCCDOpen: true,
wpCommandSucceedsWhenCCDLocked: true,
},
}, {
Name: "cap_unless_locked",
ExtraHardwareDeps: hwdep.D(hwdep.HasGSCCr50()),
Val: cCDCapabilitiesOverrideWP{
capState: servo.CapUnlessLocked,
wpCommandSucceedsWhenCCDOpen: true,
wpCommandSucceedsWhenCCDLocked: false,
},
}, {
Name: "cap_if_opened",
Val: cCDCapabilitiesOverrideWP{
capState: servo.CapIfOpened,
wpCommandSucceedsWhenCCDOpen: true,
wpCommandSucceedsWhenCCDLocked: false,
},
}},
})
}
func CCDCapabilitiesOverrideWP(ctx context.Context, s *testing.State) {
h := s.FixtValue().(*fixture.Value).Helper
userParams := s.Param().(cCDCapabilitiesOverrideWP)
if err := h.OpenCCD(ctx, true, false); err != nil {
s.Fatal("Failed to open CCD: ", err)
}
originalFWWPState, err := h.Servo.GetString(ctx, servo.FWWPState)
if err != nil {
s.Fatal("Failed to get FW WP state: ", err)
}
// Restore the original FW WP state after the test has finished
defer func() {
// Convert state to a value Servo can set
if originalFWWPState == "off" {
originalFWWPState = string(servo.FWWPStateOff)
} else if originalFWWPState == "on" {
originalFWWPState = string(servo.FWWPStateOn)
}
if err := setFWWPState(ctx, s, originalFWWPState); err != nil {
s.Fatal("Failed to restore FW WP state: ", err)
}
}()
if err := resetFWWPState(ctx, s); err != nil {
s.Fatal("Failed reset FW WP state: ", err)
}
ccdSettings := map[servo.CCDCap]servo.CCDCapState{"OverrideWP": userParams.capState}
if err := h.Servo.SetCCDCapability(ctx, ccdSettings); err != nil {
s.Fatal("Failed to set `OverrideWP` capability state: ", err)
}
if err := verifyGscWpCommand(ctx, s, userParams.wpCommandSucceedsWhenCCDOpen); err != nil {
s.Fatal("Failed verify GSC `wp` command behavior: ", err)
}
if err := resetFWWPState(ctx, s); err != nil {
s.Fatal("Failed reset FW WP state: ", err)
}
if err := h.Servo.LockCCD(ctx); err != nil {
s.Fatal("Failed to lock CCD: ", err)
}
// Open CCD when finished
defer func() {
if err := h.OpenCCD(ctx, true, false); err != nil {
s.Fatal("Failed to open CCD: ", err)
}
}()
if err := verifyGscWpCommand(ctx, s, userParams.wpCommandSucceedsWhenCCDLocked); err != nil {
s.Fatal("Failed verify GSC `wp` command behavior: ", err)
}
}
// verifyGscWpCommand attempts to use the GSC `wp` command to set WP to on then
// off to verify that the command is working properly. Servo is used to
// independently verify the WP state set by the command. If `expectSuccess` is
// false, the `wp` command is expected to fail and the WP is not checked via
// Servo.
func verifyGscWpCommand(ctx context.Context, s *testing.State, expectSuccess bool) error {
if err := runGscWpCommand(ctx, s, true, expectSuccess); err != nil {
return errors.Wrap(err, "failed to run GSC `wp` command to enable write protect")
}
if expectSuccess {
if err := verifyFWWPState(ctx, s, true); err != nil {
return errors.Wrap(err, "failed to verify FW WP state as on")
}
}
if err := runGscWpCommand(ctx, s, false, expectSuccess); err != nil {
return errors.Wrap(err, "failed to run GSC `wp` command to disabled write protect")
}
if expectSuccess {
if err := verifyFWWPState(ctx, s, false); err != nil {
return errors.Wrap(err, "failed to verify FW WP state as off")
}
}
return nil
}
// resetFWWPState uses Servo to reset the FW WP state.
func resetFWWPState(ctx context.Context, s *testing.State) error {
return setFWWPState(ctx, s, "reset")
}
// setFWWPState uses Servo to set the FW WP state.
func setFWWPState(ctx context.Context, s *testing.State, value string) error {
h := s.FixtValue().(*fixture.Value).Helper
testing.ContextLogf(ctx, "Setting %q to %q", servo.FWWPState, value)
return h.Servo.SetString(ctx, servo.FWWPState, value)
}
// runGscWpCommand attempts to call the GSC write protect command with an
// argument to set the WP to on or off. An `expectSuccess` parameter is required
// so success can be verified. An error is returned if this function fails.
func runGscWpCommand(ctx context.Context, s *testing.State, wpOn, expectSuccess bool) error {
h := s.FixtValue().(*fixture.Value).Helper
command := "wp on"
if !wpOn {
command = "wp off"
}
regex := "Flash WP:"
failureRegex := "Access Denied"
if !expectSuccess {
regex = failureRegex
}
if err := h.Servo.CheckGSCCommandOutput(ctx, command, []string{regex}); err != nil {
return errors.Wrap(err, "failed to match GSC command output, expected command `"+command+"` to succeed = "+strconv.FormatBool(expectSuccess))
}
return nil
}
// verifyFWWPState checks that the FW WP state returned via Servo matches the
// `expectOn` parameter.
func verifyFWWPState(ctx context.Context, s *testing.State, expectOn bool) error {
isFWWPStateEnabled, err := getFWWPState(ctx, s)
if err != nil {
return errors.Wrap(err, "failed to get FW WP state")
}
if isFWWPStateEnabled != expectOn {
return errors.Wrap(err, "expected FW WP state = "+strconv.FormatBool(expectOn)+", but found state = "+strconv.FormatBool(isFWWPStateEnabled))
}
return nil
}
// getFWWPState uses Servo to get the FW WP state, either `true` if enabled or
// `false` if disabled.
func getFWWPState(ctx context.Context, s *testing.State) (bool, error) {
h := s.FixtValue().(*fixture.Value).Helper
state, err := h.Servo.GetString(ctx, servo.FWWPState)
if err != nil {
return true, errors.Wrap(err, "failed to get FW WP state value")
}
switch servo.FWWPStateValue(state) {
// Strings returned when using Servo to force FW WP on/off
case servo.FWWPStateOn:
return true, nil
case servo.FWWPStateOff:
return false, nil
// Strings returned when using GSC to force FW WP on/off
case "on":
return true, nil
case "off":
return false, nil
default:
return false, errors.New("Invalid FW WP state: " + state)
}
}