Flash ec-rw from specified path
If the EC-RW image path is set, then swap it into the AP-RW before
flashing. AP-RO+EC-RW will flash during the RO update step. AP-RW+EC-RW
will flash during the RW step. And EC-RW without any AP update will read
the current AP image from the DUT, swap the EC, and flash it back during
the RW step.
When downloading ec images, also extract the ec.config, as the swap
script will want to swap it in.
Don't check the EC-RW hash if we have the EC-RW version number. The
presence of the EC-RW version appears to depend on the age of the image.
Add some TODOs for future ideas.
BUG=b:397764548
TEST=Ran several scenarios on rex/karis
Change-Id: I90e6a7458c7bb570d0d05d400404fd4db6595c2e
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/dev-util/+/6300458
Tested-by: Jeremy Bettis <jbettis@chromium.org>
Auto-Submit: Jeremy Bettis <jbettis@chromium.org>
Reviewed-by: Kshitij Shah <tij@google.com>
Commit-Queue: Jeremy Bettis <jbettis@chromium.org>
diff --git a/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/README.md b/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/README.md
index 1ea93a6..d113f69 100644
--- a/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/README.md
+++ b/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/README.md
@@ -7,6 +7,52 @@
See go/cft-fw-provision-v2 for background.
+## Input proto
+
+The request to provision comes in as a chromiumos.test.api.InstallRequest with
+the metadata field populated with
+chromiumos.test.api.FirmwareProvisionInstallMetadata. The fields in
+firmware_config can be populated in these configurations:
+
+* main_ro_payload - The AP RO and AP RW A/B will be updated with this image. As
+ a side effect, the EC RW will also be updated to the version embedded in the
+ AP RW image. The EC RO will remain untouched.
+* main_rw_payload - The AP RW A/B will be updated with this image. As
+ a side effect, the EC RW will also be updated to the version embedded in the
+ AP RW image. The AP & EC RO will remain untouched.
+* ec_ro_payload - The EC RO will be updated with this image. The AP RO/RW & EC
+ RW will be left untouched.
+* ec_rw_payload - The EC RW from this image will be embedded into the AP RW for
+ A and B, and flashed to AP RW A/B.
+
+Here are some common combinations, and where the image comes from:
+
+Payloads | AP RO | EC RO | AP RW | EC RW | Note
+-|-|-|-|-|-
+main_ro + ec_ro | main_ro | ec_ro | main_ro | main_ro | Typical for faft tests w/o EC branch
+main_ro + ec_ro + main_rw | main_ro | ec_ro | main_rw | main_rw | Typical for FW Qual w/o EC branch
+main_ro + ec_ro + main_rw + ec_rw | main_ro | ec_ro | main_rw | ec_rw | Typical for FW Qual w/ EC branch
+main_ro + ec_ro + ec_rw | main_ro | ec_ro | main_ro | ec_rw | Typical for faft tests w/ EC branch
+main_rw | - | - | main_rw | main_rw
+main_rw + ec_rw | - | - | main_rw | ec_rw
+
+After the firmware is updated, the version numbers will be checked to ensure
+the update was successful. The AP firmware version for the inactive bank (A or
+B) will not be checked, only the active firmware.
+
+### Paths
+
+The paths in the payload fields above should be a gs path to the archive
+containing the image for the correct model. However, if the image is a
+firmware_from_source archive, the code will look first for a model specific
+file in the `gs://firmware-image-archive` bucket. I.e. If you are flashing a
+brya/omnigul DUT, then a url like
+`gs://chromeos-image-archive/firmware-brya-14505.B-branch/R100-14505.832.0-1-8730368903603296945/brya/firmware_from_source.tar.bz2`
+will search for
+`gs://firmware-image-archive/firmware-brya-14505.B/14505.832.0/omnigul.14505.832.0.tar.bz2`
+first, and if it is not found, extract `image-omnigul.bin` from the giant
+`firmware_from_source.tar.bz2` archive.
+
## Launching
A prebuilt cros-fw-provision will be available in the chroot. To launch it,
@@ -68,6 +114,8 @@
```
CACHE_SERVER=192.168.100.1
FW_IMAGE=$(gsutil ls -l gs://chromeos-image-archive/firmware-{brya,glados,oak,${BOARD?}}*-branch/*/${BOARD?}/firmware_from_source.tar.bz2 | grep -v TOTAL | sort -k2 | tail -1 | awk '{print $3}')
+EC_BRANCH=$(gsutil ls -d gs://firmware-image-archive/firmware-ec-R* | tail -1)
+EC_IMAGE=$(gsutil ls -l ${EC_BRANCH}*/${BOARD}/firmware_from_source.tar.bz2 | grep -v TOTAL | sort -k2 | tail -1 | awk '{print $3}')
cat >startup.json <<STARTUP
{
"dut": {
@@ -102,6 +150,7 @@
}
}
STARTUP
+# Typical case for tip of branch testing
cat >install.json <<INSTALL
{
"metadata": {
@@ -116,7 +165,13 @@
"ecRoPayload": {
"firmwareImagePath": {
"hostType": "GS",
- "path": "${FW_IMAGE?}"
+ "path": "${EC_IMAGE?}"
+ }
+ },
+ "ecRwPayload": {
+ "firmwareImagePath": {
+ "hostType": "GS",
+ "path": "${EC_IMAGE?}"
}
}
}
@@ -128,6 +183,15 @@
~/go/bin/cros-fw-provision cli -startup startup.json -install install.json
```
+If you want to test specific versions like a firmware qual would, this command
+should help you find the path:
+
+```
+BOARD=rex
+VERSION=16151.2.19
+gsutil ls -d gs://{firmware,chromeos}-image-archive/firmware-{ec,${BOARD}}*/*${VERSION}*
+```
+
## Dependencies
cros-fw-provision expects cros-cache, and either cros-dut or cros-servod, to be
diff --git a/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/cros_fw_provision_test.go b/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/cros_fw_provision_test.go
index b0f5458..8baf66a 100644
--- a/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/cros_fw_provision_test.go
+++ b/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/cros_fw_provision_test.go
@@ -139,7 +139,7 @@
func TestDetailedRequestSSHStates(t *testing.T) {
fakeGSPath := "gs://chromeos-image-archive/board-firmware-branch/R123-12345.0.0/board/firmware_from_source.tar.bz2"
- makeRequest := func(main_rw, main_ro, ec_ro bool) *api.InstallRequest {
+ makeRequest := func(main_rw, main_ro, ec_ro, ec_rw bool) *api.InstallRequest {
fakePayload := &build_api.FirmwarePayload{FirmwareImage: &build_api.FirmwarePayload_FirmwareImagePath{FirmwareImagePath: &conf.StoragePath{HostType: conf.StoragePath_GS, Path: fakeGSPath}}}
FirmwareConfig := build_api.FirmwareConfig{}
if main_rw {
@@ -151,6 +151,9 @@
if ec_ro {
FirmwareConfig.EcRoPayload = fakePayload
}
+ if ec_rw {
+ FirmwareConfig.EcRwPayload = fakePayload
+ }
any, err := anypb.New(&api.FirmwareProvisionInstallMetadata{
FirmwareConfig: &FirmwareConfig,
})
@@ -180,12 +183,13 @@
type TestCase struct {
// inputs
- mainRw, mainRo, ecRo bool
- configYAML string
+ mainRw, mainRo, ecRo, ecRw bool
+ configYAML string
// These are the first files that will exist in the archive.
apImageWithinArchive string
ecImageWithinArchive string
npcxImageWithinArchive string
+ ecConfigWithinArchive string
// expected outputs
updateRw, updateRo bool
expectConstructorError bool
@@ -206,14 +210,14 @@
}
testCases := []TestCase{
- { /*in*/ false, false, false, "", "image.bin", "ec.bin", "npcx_monitor.bin" /*out*/, false, false /*err*/, true, fullVersion},
- { /*in*/ true, false, false, "", "image.bin", "ec.bin", "npcx_monitor.bin" /*out*/, true, false /*err*/, false, fullVersion},
- { /*in*/ false, true, false, "", "image.bin", "ec.bin", "npcx_monitor.bin" /*out*/, false, true /*err*/, false, roOnlyVersion},
- { /*in*/ false, false, true, "", "image.bin", "ec.bin", "npcx_monitor.bin" /*out*/, false, true /*err*/, false, roOnlyVersion},
- { /*in*/ false, true, true, "", "image.bin", "ec.bin", "npcx_monitor.bin" /*out*/, false, true /*err*/, false, roOnlyVersion},
- { /*in*/ true, true, true, "", "image.bin", "ec.bin", "npcx_monitor.bin" /*out*/, true, true /*err*/, false, fullVersion},
- { /*in*/ true, true, false, "", "image.bin", "ec.bin", "npcx_monitor.bin" /*out*/, true, true /*err*/, false, fullVersion},
- { /*in*/ true, true, true, `{
+ { /*in*/ false, false, false, false, "", "image.bin", "ec.bin", "npcx_monitor.bin", "ec.config" /*out*/, false, false /*err*/, true, fullVersion},
+ { /*in*/ true, false, false, false, "", "image.bin", "ec.bin", "npcx_monitor.bin", "ec.config" /*out*/, true, false /*err*/, false, fullVersion},
+ { /*in*/ false, true, false, false, "", "image.bin", "ec.bin", "npcx_monitor.bin", "ec.config" /*out*/, false, true /*err*/, false, roOnlyVersion},
+ { /*in*/ false, false, true, false, "", "image.bin", "ec.bin", "npcx_monitor.bin", "ec.config" /*out*/, false, true /*err*/, false, roOnlyVersion},
+ { /*in*/ false, true, true, false, "", "image.bin", "ec.bin", "npcx_monitor.bin", "ec.config" /*out*/, false, true /*err*/, false, roOnlyVersion},
+ { /*in*/ true, true, true, false, "", "image.bin", "ec.bin", "npcx_monitor.bin", "ec.config" /*out*/, true, true /*err*/, false, fullVersion},
+ { /*in*/ true, true, false, false, "", "image.bin", "ec.bin", "npcx_monitor.bin", "ec.config" /*out*/, true, true /*err*/, false, fullVersion},
+ { /*in*/ true, true, true, false, `{
"chromeos": {
"configs": [{
"firmware": {
@@ -227,7 +231,7 @@
"name": "test_model"
}]
}
- }`, "image-fromcoreboot.bin", "fromcoreboot/ec.bin", "fromcoreboot/npcx_monitor.bin" /*out*/, true, true /*err*/, false, fullVersion},
+ }`, "image-fromcoreboot.bin", "fromcoreboot/ec.bin", "fromcoreboot/npcx_monitor.bin", "fromcoreboot/ec.config" /*out*/, true, true /*err*/, false, fullVersion},
}
log, _ := cli.SetUpLog(cli.DefaultLogDirectory)
@@ -239,7 +243,7 @@
// Create FirmwareService.
ctx := context.Background()
- req := makeRequest(testCase.mainRw, testCase.mainRo, testCase.ecRo)
+ req := makeRequest(testCase.mainRw, testCase.mainRo, testCase.ecRo, testCase.ecRw)
log.Printf(" Test Case: %#v\n Request: {%s}", testCase, req.String())
cacheServer := url.URL{
Scheme: "http",
@@ -354,6 +358,9 @@
dsc.EXPECT().ExecCommand(gomock.Any(), newCurlMatcher("'/var/tmp/some TempDir/npcx_monitor.bin'", gomock.Eq(testCase.npcxImageWithinArchive), log)).
After(stagingCall).After(mkdirCall).Times(1).
Return(newResponse(&api.ExecCommandResponse{ExitInfo: &api.ExecCommandResponse_ExitInfo{}}), nil)
+ dsc.EXPECT().ExecCommand(gomock.Any(), newCurlMatcher("'/var/tmp/some TempDir/ec.config'", gomock.Eq(testCase.ecConfigWithinArchive), log)).
+ After(stagingCall).After(mkdirCall).Times(1).
+ Return(newResponse(&api.ExecCommandResponse{ExitInfo: &api.ExecCommandResponse_ExitInfo{}}), nil)
}
if testCase.mainRo {
dsc.EXPECT().ExecCommand(gomock.Any(), &rpcMsg{msg: &api.ExecCommandRequest{
@@ -397,6 +404,27 @@
After(stagingCall).After(mkdirCall).Times(1).
Return(newResponse(&api.ExecCommandResponse{ExitInfo: &api.ExecCommandResponse_ExitInfo{}}), nil)
}
+ if testCase.ecRw {
+ stagingCall := dsc.EXPECT().ExecCommand(gomock.Any(), &rpcMsg{msg: &api.ExecCommandRequest{
+ Command: "curl",
+ Args: []string{"-f", "-S", stagingURL},
+ }}).MinTimes(1).Return(newResponse(&api.ExecCommandResponse{ExitInfo: &api.ExecCommandResponse_ExitInfo{}}), nil)
+ dsc.EXPECT().ExecCommand(gomock.Any(), newCurlMatcher("'/var/tmp/some TempDir/ec.bin'", gomock.Eq("test_board/ec.bin"), log)).
+ After(stagingCall).After(mkdirCall).MaxTimes(1).
+ Return(newResponse(&api.ExecCommandResponse{ExitInfo: &api.ExecCommandResponse_ExitInfo{Status: 1}, Stderr: []byte("The requested URL returned error: 404\n")}), nil)
+ dsc.EXPECT().ExecCommand(gomock.Any(), newCurlMatcher("'/var/tmp/some TempDir/ec.bin'", gomock.Eq("test_model/ec.bin"), log)).
+ After(stagingCall).After(mkdirCall).MaxTimes(1).
+ Return(newResponse(&api.ExecCommandResponse{ExitInfo: &api.ExecCommandResponse_ExitInfo{Status: 1}, Stderr: []byte("The requested URL returned error: 404\n")}), nil)
+ dsc.EXPECT().ExecCommand(gomock.Any(), newCurlMatcher("'/var/tmp/some TempDir/ec.bin'", gomock.Eq(testCase.ecImageWithinArchive), log)).
+ After(stagingCall).After(mkdirCall).Times(1).
+ Return(newResponse(&api.ExecCommandResponse{ExitInfo: &api.ExecCommandResponse_ExitInfo{}}), nil)
+ dsc.EXPECT().ExecCommand(gomock.Any(), newCurlMatcher("'/var/tmp/some TempDir/npcx_monitor.bin'", gomock.Eq(testCase.npcxImageWithinArchive), log)).
+ After(stagingCall).After(mkdirCall).Times(1).
+ Return(newResponse(&api.ExecCommandResponse{ExitInfo: &api.ExecCommandResponse_ExitInfo{}}), nil)
+ dsc.EXPECT().ExecCommand(gomock.Any(), newCurlMatcher("'/var/tmp/some TempDir/ec.config'", gomock.Eq(testCase.ecConfigWithinArchive), log)).
+ After(stagingCall).After(mkdirCall).Times(1).
+ Return(newResponse(&api.ExecCommandResponse{ExitInfo: &api.ExecCommandResponse_ExitInfo{}}), nil)
+ }
unexpected := dsc.EXPECT().ExecCommand(gomock.Any(), gomock.Any()).AnyTimes().Do(func(ctx context.Context, in *api.ExecCommandRequest, opts ...grpc.CallOption) interface{} {
t.Fatalf("Unexpected ExecCommand: %s", in.String())
diff --git a/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/service/common.go b/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/service/common.go
index 5532200..339ae82 100644
--- a/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/service/common.go
+++ b/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/service/common.go
@@ -7,6 +7,7 @@
"context"
"fmt"
"log"
+ "net/url"
"path"
"regexp"
"strings"
@@ -15,12 +16,12 @@
"go.chromium.org/chromiumos/config/go/test/api"
common_utils "go.chromium.org/chromiumos/test/provision/v2/common-utils"
- "net/url"
-
"github.com/pkg/errors"
)
const curlExtractTimeout = 20 * time.Minute
+const futilityReadTimeout = 10 * time.Minute
+const swapEcRwTimeout = 5 * time.Minute
// ImageArchiveMetadata will be the value of the map in which the key is the
// gsPath, so we can avoid downloading/reprocessing same archives.
@@ -326,6 +327,19 @@
} else if !ok {
log.Printf("%q not found", npcxCandidate)
}
+ // Try to get ec.config also
+ ecConfigCandidate := strings.Replace(filename, "ec.bin", "ec.config", 1)
+ log.Printf("Trying %q", ecConfigCandidate)
+ url, err = createExtractURL(ctx, candidate.GSURL, ecConfigCandidate, fws.CacheServer)
+ if err != nil {
+ return "", errors.Wrapf(err, "no url for %q", ecConfigCandidate)
+ }
+ ecConfigPath := fmt.Sprintf("%s/ec.config", imageMetadata.ArchiveDir)
+ if ok, err := ExtractFile(ctx, dut, url.String(), ecConfigPath); err != nil {
+ return "", errors.Wrapf(err, "extract %q", ecConfigCandidate)
+ } else if !ok {
+ log.Printf("%q not found", ecConfigCandidate)
+ }
return destPath, nil
}
}
@@ -357,3 +371,22 @@
extractURL.RawQuery = v.Encode()
return extractURL, nil
}
+
+// SwapECRWImage switches the EC RW in the AP image with the specified image.
+// If the AP image path is blank, this will read the firmware from the flash.
+// Returns the path to the AP image.
+func SwapECRWImage(ctx context.Context, dut api.DutServiceClient, apImagePath, ecImagePath string) (string, error) {
+ if apImagePath == "" {
+ apImagePath = path.Join(path.Dir(ecImagePath), "swapped_bios.bin")
+ stdout, stderr, err := RunDUTCommand(ctx, dut, futilityReadTimeout, "futility", []string{"read", apImagePath}, nil)
+ if err != nil {
+ return "", errors.Wrapf(err, "futility read failed: %s %s", stdout, stderr)
+ }
+ }
+ stdout, stderr, err := RunDUTCommand(ctx, dut, swapEcRwTimeout, "/usr/share/vboot/bin/swap_ec_rw", []string{"--image", apImagePath, "--ec", ecImagePath}, nil)
+ if err != nil {
+ return "", errors.Wrapf(err, "swap_ec_rw failed: %s %s", stdout, stderr)
+ }
+
+ return apImagePath, nil
+}
diff --git a/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/service/firmwareservice.go b/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/service/firmwareservice.go
index fa15642..e85a8ac 100644
--- a/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/service/firmwareservice.go
+++ b/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/service/firmwareservice.go
@@ -35,6 +35,8 @@
Versions struct {
RO string `yaml:"ro"`
RW string `yaml:"rw"`
+ // The EC version embedded within the AP-RW image.
+ ECRW string `yaml:"ecrw"`
} `yaml:"versions"`
} `yaml:"host"`
EC struct {
@@ -56,6 +58,7 @@
mainRwPath *conf.StoragePath
mainRoPath *conf.StoragePath
ecRoPath *conf.StoragePath
+ ecRwPath *conf.StoragePath
board, model string
// The name of the model's fw binary from config.yaml
@@ -135,6 +138,7 @@
// flashed with write protection turned off.
fws.mainRoPath = detailedRequest.MainRoPayload.GetFirmwareImagePath()
fws.ecRoPath = detailedRequest.EcRoPayload.GetFirmwareImagePath()
+ fws.ecRwPath = detailedRequest.EcRwPayload.GetFirmwareImagePath()
if useServo {
fws.prepareServoConnection(ctx, servoClient)
@@ -215,6 +219,9 @@
if fws.ecRoPath != nil {
images = append(images, "EC(RO)")
}
+ if fws.ecRwPath != nil {
+ images = append(images, "EC(RW)")
+ }
informationString += strings.Join(images, " and ") + " firmware"
flashMode := "SSH"
@@ -231,7 +238,8 @@
// UpdateRw returns whether the read/write firmware is being operated on.
func (fws *FirmwareService) UpdateRw() bool {
- return fws.mainRwPath != nil
+ // If AP RW was specified, or EC RW without AP RO
+ return fws.mainRwPath != nil || (fws.ecRwPath != nil && fws.mainRoPath == nil)
}
// UpdateRo returns whether the read-only firmware is being operated on.
@@ -373,9 +381,11 @@
// If flashing over ssh, simply calls runFutility().
// If flashing over servo, also runs pre- and post-flashing dut-controls.
func (fws *FirmwareService) FlashWithFutility(ctx context.Context, rwOnly bool, futilityImageArgs []string, apImagePath, ecImagePath string) error {
+ // TODO: Remove this and move to call sites.
if err := fws.ExtractFirmwareVersions(ctx, rwOnly, futilityImageArgs, apImagePath); err != nil {
return errors.Wrap(err, "extract versions")
}
+
err := fws.sshFlash(ctx, rwOnly, futilityImageArgs, ecImagePath)
if err != nil && strings.Contains(err.Error(), "CSME_LOCKED") {
if fws.servoClient != nil {
@@ -466,6 +476,7 @@
// b/360198909: Sometimes the output contains control characters.
out = strings.ReplaceAll(out, "\x01", "")
+ log.Printf("Manifest: %s", out)
versions := versionJSON{}
err = yaml.Unmarshal([]byte(out), &versions)
@@ -482,12 +493,13 @@
if !rwOnly && versions.Default.EC.Versions.RO != "" {
fws.ExpectedVersions.EC.Versions.RO = versions.Default.EC.Versions.RO
}
- if versions.Default.EC.Versions.RW != "" {
- fws.ExpectedVersions.EC.Versions.RW = versions.Default.EC.Versions.RW
+ if versions.Default.AP.Versions.ECRW != "" && fws.board != "drallion" && fws.board != "sarien" {
+ fws.ExpectedVersions.EC.Versions.RW = versions.Default.AP.Versions.ECRW
+ fws.ExpectedVersions.EC.Versions.RWHash = ""
}
- if apImagePath != "" && fws.board != "drallion" && fws.board != "sarien" {
- // Get the EC RW hash from the AP image, since the version in the manifest may be wrong.
+ // If there is no EC RW version, get the EC RW hash from the AP image.
+ if versions.Default.AP.Versions.ECRW == "" && apImagePath != "" && fws.board != "drallion" && fws.board != "sarien" {
ecImagePath := fmt.Sprintf("%s-ecrw.hash", apImagePath)
_, err = connection.RunCmd(ctx, "cbfstool", []string{fmt.Sprintf("'%s'", apImagePath), "extract", "-r", "FW_MAIN_A", "-n", "ecrw.hash", "-f", fmt.Sprintf("'%s'", ecImagePath)})
if err != nil {
@@ -497,9 +509,9 @@
if err != nil {
return errors.Wrap(err, "failed to get ecrw hash")
}
- fws.ExpectedVersions.EC.Versions.RW = ""
fws.ExpectedVersions.EC.Versions.RWHash = strings.Join(strings.FieldsFunc(out, unicode.IsSpace), "")
}
+ log.Printf("fws.ExpectedVersions: %+v", fws.ExpectedVersions)
return nil
}
@@ -659,7 +671,7 @@
csmeLockedRe := regexp.MustCompile(`The CSME was already locked`)
// Poll every 10s for the log file to appear and have the "EXIT CODE:" string in it.
- for time.Now().Sub(startTime) < 10*time.Minute {
+ for time.Since(startTime) < 10*time.Minute {
time.Sleep(10 * time.Second)
catCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
@@ -772,6 +784,11 @@
return fws.ecRoPath.GetPath()
}
+// GetEcRwPath returns the path for the read/write portion of the EC firmware.
+func (fws *FirmwareService) GetEcRwPath() string {
+ return fws.ecRwPath.GetPath()
+}
+
type configData struct {
ChromeOS struct {
Configs []struct {
diff --git a/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/state-machine/interface.go b/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/state-machine/interface.go
index 4d9fe2b..9531d34 100644
--- a/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/state-machine/interface.go
+++ b/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/state-machine/interface.go
@@ -12,6 +12,7 @@
)
// ServiceState is a single state representation.
+// TODO: Get rid of this state machine.
type ServiceState interface {
// Execute Runs the state
Execute(ctx context.Context, log *log.Logger) (*api.FirmwareProvisionResponse, api.InstallResponse_Status, error)
diff --git a/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/state-machine/preparestate.go b/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/state-machine/preparestate.go
index 7f67f4a..60f3ae3 100644
--- a/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/state-machine/preparestate.go
+++ b/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/state-machine/preparestate.go
@@ -6,9 +6,10 @@
import (
"context"
- firmwareservice "go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/service"
"log"
+ firmwareservice "go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/service"
+
"go.chromium.org/chromiumos/config/go/test/api"
)
@@ -51,6 +52,11 @@
return nil, api.InstallResponse_STATUS_UPDATE_FIRMWARE_FAILED, firmwareservice.UpdateFirmwareFailedErr(err)
}
}
+ if ecRwPath := s.service.GetEcRwPath(); len(ecRwPath) > 0 {
+ if err := s.service.DownloadAndProcess(ctx, ecRwPath); err != nil {
+ return nil, api.InstallResponse_STATUS_UPDATE_FIRMWARE_FAILED, firmwareservice.UpdateFirmwareFailedErr(err)
+ }
+ }
return nil, api.InstallResponse_STATUS_SUCCESS, nil
}
diff --git a/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/state-machine/updaterostate.go b/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/state-machine/updaterostate.go
index 0b7b2b1..5ea5d18 100644
--- a/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/state-machine/updaterostate.go
+++ b/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/state-machine/updaterostate.go
@@ -12,6 +12,7 @@
"regexp"
"time"
+ "github.com/pkg/errors"
firmwareservice "go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/service"
"go.chromium.org/chromiumos/config/go/test/api"
@@ -29,7 +30,7 @@
// form futility command args based on the request
var futilityImageArgs []string
// Detailed Request
- var mainRoPath, ecRoPath string
+ var mainRoPath, ecRoPath, ecRwPath string
var err error
board := s.service.GetBoard()
ecRoMetadata, ok := s.service.GetImageMetadata(s.service.GetEcRoPath())
@@ -102,7 +103,26 @@
}
}
- log.Printf("[FW Provisioning: Update RO] flashing RO firmware with futility\n")
+ // If EC RW is present, but AP RW is not, and we were going to flash the AP anyway, then run swap_ec_rw on mainRoPath
+ ecRwMetadata, ok := s.service.GetImageMetadata(s.service.GetEcRwPath())
+ if mainRoPath != "" && s.service.GetMainRwPath() == "" && ok && board != "drallion" && board != "sarien" {
+ log.Printf("[FW Provisioning: Update RO] extracting EC-RW image to flash\n")
+ ecRwPath, err = firmwareservice.PickAndExtractECImage(ctx, s.service.DUTServer, ecRwMetadata, s.service.GetEcRwPath(), s.service)
+ if err != nil {
+ return nil, api.InstallResponse_STATUS_UPDATE_FIRMWARE_FAILED, firmwareservice.UpdateFirmwareFailedErr(err)
+ }
+ newMainPath, err := firmwareservice.SwapECRWImage(ctx, s.service.DUTServer, mainRoPath, ecRwPath)
+ if err != nil {
+ return nil, api.InstallResponse_STATUS_UPDATE_FIRMWARE_FAILED, firmwareservice.UpdateFirmwareFailedErr(err)
+ }
+ if newMainPath != mainRoPath {
+ return nil, api.InstallResponse_STATUS_UPDATE_FIRMWARE_FAILED, errors.Errorf("newMainPath unexpected, got %q, want %q", newMainPath, mainRoPath)
+ }
+ }
+
+ // TODO: Call fws.ExtractFirmwareVersions & fws.ActiveFirmwareVersions, and if they match, then skip flashing.
+ // TODO: Remove fws.ExtractFirmwareVersions from fws.FlashWithFutility
+ log.Printf("[FW Provisioning: Update RO] flashing RO/RW firmware with futility\n")
err = s.service.FlashWithFutility(ctx, false /* WP */, futilityImageArgs, mainRoPath, ecRoPath)
if err != nil {
return nil, api.InstallResponse_STATUS_UPDATE_FIRMWARE_FAILED, firmwareservice.UpdateFirmwareFailedErr(err)
diff --git a/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/state-machine/updaterwstate.go b/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/state-machine/updaterwstate.go
index 2d31c05..cf95f62 100644
--- a/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/state-machine/updaterwstate.go
+++ b/src/go.chromium.org/chromiumos/test/provision/v2/cros-fw-provision/state-machine/updaterwstate.go
@@ -24,23 +24,47 @@
func (s FirmwareUpdateRwState) Execute(ctx context.Context, log *log.Logger) (*api.FirmwareProvisionResponse, api.InstallResponse_Status, error) {
// form futility command args based on the request
var futilityImageArgs []string
- // Detailed Request
- mainRwMetadata, ok := s.service.GetImageMetadata(s.service.GetMainRwPath())
- if !ok {
- panic(ok) // if nil, current state of the statemachine should not started
- }
- log.Printf("[FW Provisioning: Update RW] extracting AP image to flash\n")
- mainRwPath, err := firmwareservice.PickAndExtractMainImage(ctx, s.service.DUTServer, mainRwMetadata, s.service.GetMainRwPath(), s.service)
- if err != nil {
- return nil, api.InstallResponse_STATUS_UPDATE_FIRMWARE_FAILED, firmwareservice.UpdateFirmwareFailedErr(err)
- }
- futilityImageArgs = []string{fmt.Sprint("--image=", mainRwPath)}
+ var mainRwPath, ecRwPath string
+ var err error
+ // Get AP Image
+ mainRwMetadata, ok := s.service.GetImageMetadata(s.service.GetMainRwPath())
+ if ok {
+ log.Printf("[FW Provisioning: Update RW] extracting AP image to flash\n")
+ mainRwPath, err = firmwareservice.PickAndExtractMainImage(ctx, s.service.DUTServer, mainRwMetadata, s.service.GetMainRwPath(), s.service)
+ if err != nil {
+ return nil, api.InstallResponse_STATUS_UPDATE_FIRMWARE_FAILED, firmwareservice.UpdateFirmwareFailedErr(err)
+ }
+ futilityImageArgs = []string{fmt.Sprint("--image=", mainRwPath)}
+ }
+
+ // Get EC Image
+ ecRwMetadata, ok := s.service.GetImageMetadata(s.service.GetEcRwPath())
+ if ok {
+ log.Printf("[FW Provisioning: Update RW] extracting EC-RW image to flash\n")
+ ecRwPath, err = firmwareservice.PickAndExtractECImage(ctx, s.service.DUTServer, ecRwMetadata, s.service.GetEcRwPath(), s.service)
+ if err != nil {
+ return nil, api.InstallResponse_STATUS_UPDATE_FIRMWARE_FAILED, firmwareservice.UpdateFirmwareFailedErr(err)
+ }
+
+ newMainPath, err := firmwareservice.SwapECRWImage(ctx, s.service.DUTServer, mainRwPath, ecRwPath)
+ if err != nil {
+ return nil, api.InstallResponse_STATUS_UPDATE_FIRMWARE_FAILED, firmwareservice.UpdateFirmwareFailedErr(err)
+ }
+ if newMainPath != mainRwPath {
+ mainRwPath = newMainPath
+ futilityImageArgs = []string{fmt.Sprint("--image=", mainRwPath)}
+ }
+ }
+
+ // TODO: Call fws.ExtractFirmwareVersions & fws.ActiveFirmwareVersions, and if they match, then skip flashing.
+ // TODO: Remove fws.ExtractFirmwareVersions from fws.FlashWithFutility
log.Printf("[FW Provisioning: Update RW] flashing RW firmware with futility\n")
err = s.service.FlashWithFutility(ctx, true /* WP */, futilityImageArgs, mainRwPath, "")
if err != nil {
return nil, api.InstallResponse_STATUS_UPDATE_FIRMWARE_FAILED, firmwareservice.UpdateFirmwareFailedErr(err)
}
+
return nil, api.InstallResponse_STATUS_SUCCESS, nil
}