blob: b7f648e1edab5dbf11c41f58b5cb3f6447a4a7c1 [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 firmware
import (
"context"
"path/filepath"
"strconv"
"time"
fwCommon "go.chromium.org/tast-tests/cros/common/firmware"
"go.chromium.org/tast-tests/cros/common/servo"
"go.chromium.org/tast-tests/cros/remote/firmware"
"go.chromium.org/tast-tests/cros/remote/firmware/fixture"
"go.chromium.org/tast/core/ctxutil"
"go.chromium.org/tast/core/errors"
"go.chromium.org/tast/core/ssh"
"go.chromium.org/tast/core/testing"
"go.chromium.org/tast/core/testing/hwdep"
)
type devBootUSBTrigger int
const (
triggerUSBBootWithKeyboard devBootUSBTrigger = iota
triggerUSBBootWithMenu
triggerUSBBootWithButton
triggerUSBBootLateInsert
)
type devBootUSBParam struct {
trigger devBootUSBTrigger
}
func init() {
testing.AddTest(&testing.Test{
Func: DevBootUSB,
Desc: "Verify that boot-up from USB works from various triggers",
Contacts: []string{
"chromeos-faft@google.com",
"cienet-firmware@cienet.corp-partner.google.com",
},
BugComponent: "b:792402", // ChromeOS > Platform > Enablement > Firmware > FAFT
Attr: []string{"group:firmware", "firmware_bios", "firmware_level2", "firmware_usb"},
Requirements: []string{"sys-fw-0021-v01", "sys-fw-0024-v01", "sys-fw-0025-v01"},
SoftwareDeps: []string{"crossystem"},
Vars: []string{"firmware.skipFlashUSB"},
Fixture: fixture.DevMode,
Timeout: 2 * time.Hour,
LacrosStatus: testing.LacrosVariantUnneeded,
Params: []testing.Param{{
Name: "keyboard",
Val: &devBootUSBParam{
trigger: triggerUSBBootWithKeyboard,
},
}, {
Name: "menu",
Val: &devBootUSBParam{
trigger: triggerUSBBootWithMenu,
},
ExtraHardwareDeps: hwdep.D(hwdep.FirmwareUIType(hwdep.LegacyMenuUI, hwdep.MenuUI)),
}, {
Name: "button",
Val: &devBootUSBParam{
trigger: triggerUSBBootWithButton,
},
ExtraHardwareDeps: hwdep.D(hwdep.FormFactor(hwdep.Detachable)),
}, {
Name: "late_insert",
Val: &devBootUSBParam{
trigger: triggerUSBBootLateInsert,
},
ExtraHardwareDeps: hwdep.D(hwdep.FirmwareUIType(hwdep.LegacyMenuUI, hwdep.LegacyClamshellUI)),
},
}})
}
func DevBootUSB(ctx context.Context, s *testing.State) {
h := s.FixtValue().(*fixture.Value).Helper
if err := h.RequireServo(ctx); err != nil {
s.Fatal("Failed to init servo: ", err)
}
if err := h.RequireConfig(ctx); err != nil {
s.Fatal("Failed to create config: ", err)
}
testOpt := s.Param().(*devBootUSBParam)
// Set up USB when there is one present, and
// for cases that depend on it.
s.Log("Setup USB key")
skipFlashUSB := false
if skipFlashUSBStr, ok := s.Var("firmware.skipFlashUSB"); ok {
var err error
skipFlashUSB, err = strconv.ParseBool(skipFlashUSBStr)
if err != nil {
s.Fatalf("Invalid value for var firmware.skipFlashUSB: got %q, want true/false", skipFlashUSBStr)
}
}
var cs *testing.CloudStorage
if !skipFlashUSB {
cs = s.CloudStorage()
}
if err := h.SetupUSBKey(ctx, cs); err != nil {
s.Fatal("USBKey not working: ", err)
}
s.Log("Enabling dev_boot_usb")
if err := h.DUT.Conn().CommandContext(ctx, "crossystem", "dev_boot_usb=1").Run(); err != nil {
s.Fatal("Failed to set crossystem dev_boot_usb to 1: ", err)
}
cleanupCtx := ctx
ctx, cancel := ctxutil.Shorten(ctx, 4*time.Minute)
defer cancel()
// Set dev_boot_usb back to 0 at the end of the test.
defer func(ctx context.Context) {
if err := h.EnsureDUTBooted(ctx); err != nil {
s.Fatal("Failed to reconnect to dut: ", err)
}
if err := h.DUT.Conn().CommandContext(ctx, "crossystem", "dev_boot_usb=0").Run(ssh.DumpLogOnError); err != nil {
s.Fatal("Failed to set crossystem dev_boot_usb to 0: ", err)
}
}(cleanupCtx)
s.Log("Removing USB from DUT")
if err := h.Servo.SetUSBMuxState(ctx, servo.USBMuxHost); err != nil {
s.Fatal("Failed to insert USB to DUT: ", err)
}
s.Log("Rebooting DUT to developer screen")
if err := h.Servo.SetPowerState(ctx, servo.PowerStateWarmReset); err != nil {
s.Fatal("Failed to cold reset dut: ", err)
}
s.Logf("Sleeping for %s (FirmwareScreen) ", h.Config.FirmwareScreen)
// GoBigSleepLint: Delay to wait for the firmware screen during boot-up.
if err := testing.Sleep(ctx, h.Config.FirmwareScreen); err != nil {
s.Fatalf("Failed to sleep for %s: %v", h.Config.FirmwareScreen, err)
}
// Pressing Ctrl-U should invoke a beep sound, indicating that
// there is no USB device.
if testOpt.trigger == triggerUSBBootLateInsert {
s.Log("Pressing Ctrl-U")
if err := h.Servo.KeypressWithDuration(ctx, servo.CtrlU, servo.DurTab); err != nil {
s.Fatal("Failed to press Ctrl-U: ", err)
}
}
s.Log("Resetting firmware screen timeout")
if err := h.Servo.PressKey(ctx, " ", servo.DurTab); err != nil {
s.Fatal("Failed to press space key: ", err)
}
s.Log("Inserting a valid USB to DUT")
if err := h.Servo.SetUSBMuxState(ctx, servo.USBMuxDUT); err != nil {
s.Fatal("Failed to insert USB to DUT: ", err)
}
// GoBigSleepLint: It may take some time for usb mux state to
// take effect.
if err := testing.Sleep(ctx, firmware.UsbVisibleTime); err != nil {
s.Fatalf("Failed to sleep for %v s: %v", firmware.UsbDisableTime, err)
}
// On KeyboardDevSwitcher machines, pressing space triggers the
// to_norm screen. Revert to the developer screen with the
// esc key.
if h.Config.ModeSwitcherType == firmware.KeyboardDevSwitcher {
if err := h.Servo.PressKey(ctx, "<esc>", servo.DurTab); err != nil {
s.Fatal("Failed to press esc: ", err)
}
// GoBigSleepLint: Sleep for model specific time.
if err := testing.Sleep(ctx, h.Config.KeypressDelay); err != nil {
s.Fatalf("Failed to sleep for %s (KeypressDelay): %v", h.Config.KeypressDelay, err)
}
}
switch testOpt.trigger {
case triggerUSBBootWithKeyboard, triggerUSBBootLateInsert:
s.Log("Pressing Ctrl-U")
if err := h.Servo.KeypressWithDuration(ctx, servo.CtrlU, servo.DurTab); err != nil {
s.Fatal("Failed to press Ctrl-U: ", err)
}
case triggerUSBBootWithMenu:
menuBypasser, err := firmware.NewMenuBypasser(ctx, h)
if err != nil {
s.Fatal("Failed to build new menuBypasser: ", err)
}
if err := menuBypasser.BypassDevBootUSB(ctx); err != nil {
s.Fatal(err, "Failed to bypass dev mode: ", err)
}
case triggerUSBBootWithButton:
s.Log("Pressing and holding volume up button for 5 seconds")
if err := h.Servo.SetInt(ctx, servo.VolumeUpHold, 5000); err != nil {
s.Fatal("Failed to press volume up: ", err)
}
}
s.Log("Waiting for DUT to reconnect")
waitConnectCtx, cancelWaitConnect := context.WithTimeout(ctx, h.Config.USBImageBootTimeout)
defer cancelWaitConnect()
if err := h.WaitConnect(waitConnectCtx, firmware.ResetEthernetDongle); err != nil {
s.Fatal("Failed to reconnect to DUT: ", err)
}
s.Log("Checking if DUT is in usbdev mode")
isUSBDev, err := h.Reporter.CheckBootMode(ctx, fwCommon.BootModeUSBDev)
if err != nil {
s.Fatal("Failed to get dut boot mode: ", err)
}
if !isUSBDev {
s.Fatal("Failed to boot from the USB")
}
if testOpt.trigger == triggerUSBBootLateInsert {
s.Log("Verifying that Ctrl-U was ineffective when there was no USB plugged")
lateInsertMsg := map[firmware.ModeSwitcherType]string{
firmware.TabletDetachableSwitcher: "No bootable kernel found on USB/SD",
firmware.KeyboardDevSwitcher: "no kernel found on USB",
}
output, err := h.Reporter.GetCBMEMLogs(ctx)
if err != nil {
s.Fatal("Failed to get CBMEM logs: ", err)
}
if err := h.ScanWithoutExpectedSequenceInSource(ctx, output, []string{lateInsertMsg[h.Config.ModeSwitcherType]}); err != nil {
saveLogPath := filepath.Join(s.OutDir(), "firmware.log")
if saveErr := h.SaveCBMEMLogs(ctx, saveLogPath); saveErr != nil {
err = errors.Wrap(saveErr, err.Error())
}
s.Fatalf("Failed to find %s from the CBMEM logs: %v", lateInsertMsg[h.Config.ModeSwitcherType], err)
}
}
}