blob: a466cf135b28311f5d805f8aba483f7cbb64c2ef [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 intel
import (
"context"
"fmt"
"regexp"
"time"
"github.com/google/uuid"
"go.chromium.org/tast-tests/cros/remote/dutfs"
"go.chromium.org/tast/core/dut"
"go.chromium.org/tast/core/errors"
"go.chromium.org/tast/core/rpc"
"go.chromium.org/tast/core/testing"
)
func init() {
testing.AddFixture(&testing.Fixture{
Name: "enableDisablePsrFixture",
Desc: "Makes PSR status to DEEP_SLEEP",
Contacts: []string{"intel.chrome.automation.team@intel.com", "ambalavanan.m.m@intel.com"},
BugComponent: "b:157291", // ChromeOS > External > Intel
Impl: &psr{},
SetUpTimeout: 5 * time.Minute,
TearDownTimeout: 2 * time.Minute,
})
}
type psr struct {
psrState string
}
// SetUp will enable PSR and reboots the DUT if required.
// This fixture will enable PSR2 which is a reversible action, which
// can be cleaned in Teardown.
func (i *psr) SetUp(ctx context.Context, s *testing.FixtState) interface{} {
d := s.DUT()
// Attempt to connect to those DUTs that aren't already connected.
if !d.Connected(ctx) {
s.Log("Attempting to connect to DUT")
if err := testing.Poll(ctx, func(ctx context.Context) error {
return d.Connect(ctx)
}, &testing.PollOptions{Interval: time.Second, Timeout: 30 * time.Second}); err != nil {
s.Fatal("Failed to connect to DUT: ", err)
}
s.Log("Connected to DUT")
}
// Connect to local gRPC services, and keep connection alive until after
rpcClient, err := rpc.Dial(s.FixtContext(), s.DUT(), s.RPCHint())
if err != nil {
s.Fatal("Failed to connect to the local gRPC service on the DUT: ", err)
}
defer func() {
if err := rpcClient.Close(ctx); err != nil {
s.Fatal("Failed to close gRPC connection to DUT: ", err)
}
}()
client := dutfs.NewClient(rpcClient.Conn)
psrState, err := enablePSR(ctx, d, client)
if err != nil {
s.Fatal("Failed to enable PSR: ", err)
}
i.psrState = psrState
if err := d.Reboot(ctx); err != nil {
return errors.Wrap(err, "failed to reboot")
}
return nil
}
// editKernelArgs is a helper function for editing kernel args. Function |f|
// performs the editing action by transforming the content of saved config.
func editKernelArgs(ctx context.Context, dut *dut.DUT, client *dutfs.Client, f func([]byte) ([]byte, error)) error {
// Save the current boot config to |prefix|.|part| (make_dev_ssd.sh saves the content to a file named |prefix|.|part|).
prefix := "/tmp/kargs" + uuid.New().String()
err := dut.Conn().CommandContext(ctx, "/usr/share/vboot/bin/make_dev_ssd.sh", "--save_config", prefix, "--partitions", "2").Run()
if err != nil {
return errors.Wrap(err, "failed to save boot config")
}
savedKArgsFile := prefix + "." + "2"
defer client.Remove(ctx, savedKArgsFile)
savedKArgs, err := client.ReadFile(ctx, savedKArgsFile)
if err != nil {
return errors.Wrap(err, "failed to read saved kernel config")
}
// Transform the content.
savedKArgs, err = f(savedKArgs)
if err != nil {
return errors.Wrap(err, "failed to transform content in boot config")
}
err = client.WriteFile(ctx, savedKArgsFile, savedKArgs, 0644)
if err != nil {
return errors.Wrap(err, "failed to edit saved kernel config")
}
err = dut.Conn().CommandContext(ctx, "/usr/share/vboot/bin/make_dev_ssd.sh", "--set_config", prefix, "--partitions", "2").Run()
if err != nil {
return errors.Wrap(err, "failed to set boot config")
}
return nil
}
// enablePSR adds args to kernel cmdline to enable PSR 2.
func enablePSR(ctx context.Context, dut *dut.DUT, client *dutfs.Client) (string, error) {
var psrValue string
if err := editKernelArgs(ctx, dut, client, func(b []byte) ([]byte, error) {
info := string(b)
re := regexp.MustCompile(`\b` + `i915.enable_psr=(\d{1})` + `\b`)
enableVal := re.FindStringSubmatch(info)
if len(enableVal) < 1 {
return nil, errors.New("failed to get i915.enable_psr value")
}
psrValue = enableVal[1]
info = re.ReplaceAllString(info, "i915.enable_psr=2")
return []byte(info), nil
}); err != nil {
return "", errors.Wrap(err, "fail to enable PSR")
}
return psrValue, nil
}
// disablePSR removes args from kernel cmdline to revert back to initial PSR.
func disablePSR(ctx context.Context, dut *dut.DUT, psrValue string, client *dutfs.Client) error {
if err := editKernelArgs(ctx, dut, client, func(b []byte) ([]byte, error) {
info := string(b)
re := regexp.MustCompile(`\bi915.enable_psr=\d{1}` + `\b`)
if psrValue != "" {
info = re.ReplaceAllString(info, fmt.Sprintf("i915.enable_psr=%s", psrValue))
}
return []byte(info), nil
}); err != nil {
return errors.Wrap(err, "failed to disable PSR")
}
return nil
}
func (i *psr) TearDown(ctx context.Context, s *testing.FixtState) {
d := s.DUT()
// Attempt to connect to those DUTs that aren't already connected.
if !d.Connected(ctx) {
s.Log("Attempting to connect to DUT")
if err := testing.Poll(ctx, func(ctx context.Context) error {
return d.Connect(ctx)
}, &testing.PollOptions{Interval: time.Second, Timeout: 30 * time.Second}); err != nil {
s.Fatal("Failed to connect to DUT: ", err)
}
}
// Connect to local gRPC services, and keep connection alive until after
// TearDown is called by using the fixture context.
rpcClient, err := rpc.Dial(s.FixtContext(), s.DUT(), s.RPCHint())
if err != nil {
s.Fatal("Failed to connect to the local gRPC service on the DUT: ", err)
}
defer func() {
if err := rpcClient.Close(ctx); err != nil {
s.Fatal("Failed to close gRPC connection to DUT: ", err)
}
}()
client := dutfs.NewClient(rpcClient.Conn)
if err := disablePSR(ctx, d, i.psrState, client); err != nil {
s.Fatal("Failed to disable PSR and revert back to initial PSR: ", err)
}
if err := d.Reboot(ctx); err != nil {
s.Fatal("Failed to reboot: ", err)
}
}
func (i *psr) Reset(ctx context.Context) error {
return nil
}
func (i *psr) PreTest(ctx context.Context, s *testing.FixtTestState) {
// No-op.
}
func (i *psr) PostTest(ctx context.Context, s *testing.FixtTestState) {
// No-op.
}