Skip to content

Commit

Permalink
Ensure BMC image is compatible with MB CPLD version
Browse files Browse the repository at this point in the history
Summary: grandteton versions v2024.11.1 require MB CPLD version `20010` or greater.  Added `checks_and_remediations` step to fail the upgrade if the MB CPLD version is incompatible with the image version.

Test Plan:
Unit tests, push flashy/bmc images to grandteton units e.g:

```
$ scripts/run_flashy_remote.sh --device mtd:flash1 --imagepath /tmp/tmp.H8gzWMVfip/openbmc.image.grandteton\:v2024.11.1/flash-grandteton --host twshared39604-oob.02.nao6.facebook.com --dry-run
```
Run step on a BMC with image version = v2024.11.1 and old MB CPLD (should fail):
```
#  /run/flashy/flashy --install && /run/flashy/checks_and_remediations/grandteton/00_ensure_compatible_mb_cpld_version --imagepath /run/upgrade/image --device mtd:flash1
...
{"message":"S365492: Image version ('grandteton-v2024.11.1') requires MB CPLD version 20010 or greater (current MB CPLD version is 20006)"}
```

Run step on a BMC with image version = v2024.11.1 and new MB CPLD (should succeed):
```
/run/flashy/flashy --install && /run/flashy/checks_and_remediations/grandteton/00_ensure_compatible_mb_cpld_version --imagepath /run/upgrade/image --device mtd:flash1
...
2024/04/09 08:33:48 MB CPLD version 20010 is compatible with image version grandteton-v2024.11.1
```

Run step on a BMC with image version < v2024.11.1 and old MB CPLD (should succeed):
```
#  /run/flashy/flashy --install && /run/flashy/checks_and_remediations/grandteton/00_ensure_compatible_mb_cpld_version --imagepath /run/upgrade/image --device mtd:flash1
...
2024/04/09 08:35:46 MB CPLD version 20006 is compatible with image version grandteton-v2023.39.2
```

Run step on a BMC with image version < v2024.11.1 and new MB CPLD (should succeed):
```
#  /run/flashy/flashy --install && /run/flashy/checks_and_remediations/grandteton/00_ensure_compatible_mb_cpld_version --imagepath /run/upgrade/image --device mtd:flash1
...
2024/04/09 08:40:29 MB CPLD version 20010 is compatible with image version grandteton-v2023.39.2
```

Reviewed By: doranand

Differential Revision: D55921434

fbshipit-source-id: 8672d76c8f4ef18978cda8826f73395b1309b50c
  • Loading branch information
kawmarco authored and facebook-github-bot committed Apr 10, 2024
1 parent ea2fc8c commit 54734f8
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/**
* Copyright 2020-present Facebook. All Rights Reserved.
*
* This program file is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program in a file named COPYING; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/

package remediations_grandteton

import (
"encoding/json"
"log"
"regexp"
"strconv"
"time"

"github.com/facebook/openbmc/tools/flashy/lib/step"
"github.com/facebook/openbmc/tools/flashy/lib/utils"
"github.com/facebook/openbmc/tools/flashy/lib/validate"
"github.com/pkg/errors"
)

func init() {
step.RegisterStep(ensureCompatibleMBCPLDVersion)
}

type FWUtilVersionJson struct {
COMPONENT string
FRU string
PRETTY_COMPONENT string
VERSION string
}

var versionRegex = regexp.MustCompile("^grandteton-(v[0-9]+[.][0-9]+[.][0-9]+)$")

var GetOpenBMCVersionFromImageFile = validate.GetOpenBMCVersionFromImageFile

// ensureCompatibleMBCPLDVersion checks whether the MB CPLD version is compatible with the image being
// flashed
func ensureCompatibleMBCPLDVersion(stepParams step.StepParams) step.StepExitError {
// Fetch current MB CPLD version from fw-util
mbCpldVersion, stepErr := fwutilGetMbCpldVersion()
if stepErr != nil {
return stepErr
}

// Fetch BMC version from image file
imageFileVer, err := GetOpenBMCVersionFromImageFile(stepParams.ImageFilePath)
if err != nil {
return step.ExitSafeToReboot{
Err: err,
}
}

if !versionRegex.MatchString(imageFileVer) {
return step.ExitSafeToReboot{
Err: errors.Errorf("Image version '%v' does not match expected format %v", imageFileVer, versionRegex),
}
}

// S365492: Images newer than v2024.11.1 require MB CPLD version 20010 or greater, otherwise sensor data
// becomes unavailable (T175831581). Booting older images with the new CPLD version should be safe.
if imageFileVer >= "grandteton-v2024.11.1" && mbCpldVersion < 20010 {
return step.ExitSafeToReboot{
Err: errors.Errorf("S365492: Image version ('%v') requires MB CPLD version 20010 or greater (current MB CPLD version is %d)", imageFileVer, mbCpldVersion),
}
}

log.Printf("MB CPLD version %d is compatible with image version %v", mbCpldVersion, imageFileVer)

return nil
}

func fwutilGetMbCpldVersion() (int, step.StepExitError) {
// Run fw-util
fwutilCmd := []string{
"fw-util", "mb", "--version-json", "mb_cpld",
}
exitCode, err, stdout, stderr := utils.RunCommand(fwutilCmd, 1800*time.Second)

if err != nil {
return 0, step.ExitSafeToReboot{
Err: errors.Errorf("`fw-util mb --version-json mb_cpld` failed with exit code %d, stderr: %v, stdout: %v", exitCode, stderr, stdout),
}
}

var fwutilVersionJson []FWUtilVersionJson
if err := json.Unmarshal([]byte(stdout), &fwutilVersionJson); err != nil {
return 0, step.ExitSafeToReboot{
Err: errors.Errorf("failed to parse `fw-util mb --version-json mb_cpld` output: %v", err),
}
}

if len(fwutilVersionJson) != 1 {
return 0, step.ExitSafeToReboot{
Err: errors.Errorf("`fw-util mb --version-json mb_cpld` returned %v entries", len(fwutilVersionJson)),
}
}

mbCpldVersion, err := strconv.Atoi(fwutilVersionJson[0].VERSION)
if err != nil {
return 0, step.ExitSafeToReboot{
Err: errors.Errorf("Cannot parse `fw-util mb --version-json mb_cpld` version as integer: %v", fwutilVersionJson),
}
}

return mbCpldVersion, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package remediations_grandteton

import (
"strings"
"testing"
"time"

"github.com/facebook/openbmc/tools/flashy/lib/step"
"github.com/facebook/openbmc/tools/flashy/lib/utils"
"github.com/pkg/errors"
)

var fwUtilOutputOldCpldExample = `
[
{
"COMPONENT": "mb_cpld",
"FRU": "mb",
"PRETTY_COMPONENT": "MB_CPLD",
"VERSION": "00020008"
}
]
`

var fwUtilOutputNewCpldExample = `
[
{
"COMPONENT": "mb_cpld",
"FRU": "mb",
"PRETTY_COMPONENT": "MB_CPLD",
"VERSION": "00020010"
}
]
`

func TestEnsureCompatibleMBCPLDVersion(t *testing.T) {
// mock and defer restore RunCommand
runCommandOrig := utils.RunCommand
getOpenBMCVersionFromImageFileOrig := GetOpenBMCVersionFromImageFile
defer func() {
utils.RunCommand = runCommandOrig
GetOpenBMCVersionFromImageFile = getOpenBMCVersionFromImageFileOrig
}()
cases := []struct {
name string
bmcImageVersion string
runCmdStdout string
want step.StepExitError
}{
{
name: "succeeded as image version == v2023.39.2 and CPLD is old",
bmcImageVersion: "grandteton-v2023.39.2",
runCmdStdout: fwUtilOutputOldCpldExample,
want: nil,
},
{
name: "succeeded as image version == v2024.11.1 and CPLD is new",
bmcImageVersion: "grandteton-v2024.11.1",
runCmdStdout: fwUtilOutputNewCpldExample,
want: nil,
},
{
name: "succeeded as image version > v2024.11.1 and CPLD is new",
bmcImageVersion: "grandteton-v2024.12.1",
runCmdStdout: fwUtilOutputNewCpldExample,
want: nil,
},
{
name: "failed as image version == v2024.11.1 but CPLD is old",
bmcImageVersion: "grandteton-v2024.11.1",
runCmdStdout: fwUtilOutputOldCpldExample,
want: step.ExitSafeToReboot{
Err: errors.Errorf("S365492: Image version ('grandteton-v2024.11.1') requires MB CPLD version 20010 or greater (current MB CPLD version is 20008)"),
},
},
{
name: "failed as image version > v2024.11.1 but CPLD is old",
bmcImageVersion: "grandteton-v2024.12.0",
runCmdStdout: fwUtilOutputOldCpldExample,
want: step.ExitSafeToReboot{
Err: errors.Errorf("S365492: Image version ('grandteton-v2024.12.0') requires MB CPLD version 20010 or greater (current MB CPLD version is 20008)"),
},
},
{
name: "invalid image version",
bmcImageVersion: "fby3-v2024.12.0",
runCmdStdout: fwUtilOutputOldCpldExample,
want: step.ExitSafeToReboot{
Err: errors.Errorf("Image version 'fby3-v2024.12.0' does not match expected format ^grandteton-(v[0-9]+[.][0-9]+[.][0-9]+)$"),
},
},
}

wantCmd := "fw-util mb --version-json mb_cpld"
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
utils.RunCommand = func(cmdArr []string, timeout time.Duration) (int, error, string, string) {
gotCmd := strings.Join(cmdArr, " ")
if wantCmd != gotCmd {
t.Errorf("cmd: want '%v' got '%v'", wantCmd, gotCmd)
}
return 0, nil, tc.runCmdStdout, ""
}
GetOpenBMCVersionFromImageFile = func(imageFile string) (string, error) {
return tc.bmcImageVersion, nil
}
got := ensureCompatibleMBCPLDVersion(step.StepParams{})
step.CompareTestExitErrors(tc.want, got, t)
})
}
}
1 change: 1 addition & 0 deletions tools/flashy/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
_ "github.com/facebook/openbmc/tools/flashy/checks_and_remediations/galaxy100"
_ "github.com/facebook/openbmc/tools/flashy/checks_and_remediations/wedge100"
_ "github.com/facebook/openbmc/tools/flashy/checks_and_remediations/yamp"
_ "github.com/facebook/openbmc/tools/flashy/checks_and_remediations/grandteton"
_ "github.com/facebook/openbmc/tools/flashy/flash_procedure"
_ "github.com/facebook/openbmc/tools/flashy/utilities"
)
Expand Down
6 changes: 3 additions & 3 deletions tools/flashy/lib/validate/compatibility.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ var CheckImageBuildNameCompatibility = func(imageFilePath string) error {
}
log.Printf("OpenBMC Version from /etc/issue: '%v'", etcIssueVer)

imageFileVer, err := getOpenBMCVersionFromImageFile(imageFilePath)
imageFileVer, err := GetOpenBMCVersionFromImageFile(imageFilePath)
if err != nil {
return err
}
Expand Down Expand Up @@ -109,11 +109,11 @@ var getNormalizedBuildNameFromVersion = func(ver string) (string, error) {
return verMap[rBuildname], nil
}

// getOpenBMCVersionFromImageFile gets OpenBMC version from the image file.
// GetOpenBMCVersionFromImageFile gets OpenBMC version from the image file.
// examples: fbtp-v2020.09.1, wedge100-v2020.07.1
// WARNING: This relies on the U-Boot version string on the image
// there is no guarantee that this will succeed
var getOpenBMCVersionFromImageFile = func(imageFilePath string) (string, error) {
var GetOpenBMCVersionFromImageFile = func(imageFilePath string) (string, error) {
// mmap the first 1MB of the image file
imageFileBuf, err := fileutils.MmapFileRange(
imageFilePath, 0, 1024*1024, syscall.PROT_READ, syscall.MAP_SHARED,
Expand Down
8 changes: 4 additions & 4 deletions tools/flashy/lib/validate/compatibility_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ func TestCompatibleVersionMapping(t *testing.T) {

func TestCheckImageBuildNameCompatibility(t *testing.T) {
getOpenBMCVersionFromIssueFileOrig := utils.GetOpenBMCVersionFromIssueFile
getOpenBMCVersionFromImageFileOrig := getOpenBMCVersionFromImageFile
getOpenBMCVersionFromImageFileOrig := GetOpenBMCVersionFromImageFile
compatibleVersionMappingOrig := compatibleVersionMapping
defer func() {
utils.GetOpenBMCVersionFromIssueFile = getOpenBMCVersionFromIssueFileOrig
getOpenBMCVersionFromImageFile = getOpenBMCVersionFromImageFileOrig
GetOpenBMCVersionFromImageFile = getOpenBMCVersionFromImageFileOrig
compatibleVersionMapping = compatibleVersionMappingOrig
}()

Expand Down Expand Up @@ -112,7 +112,7 @@ func TestCheckImageBuildNameCompatibility(t *testing.T) {
utils.GetOpenBMCVersionFromIssueFile = func() (string, error) {
return tc.etcIssueVer, nil
}
getOpenBMCVersionFromImageFile = func(imageFilePath string) (string, error) {
GetOpenBMCVersionFromImageFile = func(imageFilePath string) (string, error) {
if exampleImageFilePath != imageFilePath {
t.Errorf("imageFilePath: want '%v' got '%v'", exampleImageFilePath, imageFilePath)
}
Expand Down Expand Up @@ -241,7 +241,7 @@ func TestGetOpenBMCVersionFromImageFile(t *testing.T) {
}
return tc.fileBuf, tc.mmapErr
}
got, err := getOpenBMCVersionFromImageFile("x")
got, err := GetOpenBMCVersionFromImageFile("x")
if tc.want != got {
t.Errorf("want '%v' got '%v'", tc.want, got)
}
Expand Down

0 comments on commit 54734f8

Please sign in to comment.