blob: ff23697d2d1617e4c536b6da51a79e954679957f [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Package tlslib provides the canonical implementation of a common TLS server.
package tlslib
import (
"context"
"encoding/json"
"fmt"
"log"
"regexp"
"strings"
"golang.org/x/crypto/ssh"
)
const (
firmwareUpdaterPath = "/usr/sbin/chromeos-firmwareupdate"
)
type FirmwareManifest map[string]FirmwareManifestData
type FirmwareManifestData struct {
Host struct {
Versions struct {
Ro string `json:"ro"`
Rw string `json:"rw"`
} `json:"versions"`
Keys struct {
Root string `json:"root"`
Recovery string `json:"recovery"`
} `json:"keys"`
Image string `json:"image"`
} `json:"host"`
Ec struct {
Versions struct {
Ro string `json:"ro"`
Rw string `json:"rw"`
} `json:"versions"`
Image string `json:"image"`
} `json:"ec"`
SignatureId string `json:"signature_id"`
}
var firmwareManifestRegexp = regexp.MustCompile("FIRMWARE_MANIFEST_KEY='(.*)'")
// getAvailableFirmwareVersion read firmware manifest from current OS and extract available firmware version based on model.
func getAvailableFirmwareVersion(c *ssh.Client) (string, error) {
out, err := runCmdOutput(c, fmt.Sprintf("%s --manifest", firmwareUpdaterPath))
if err != nil {
return "", fmt.Errorf("getAvailableFirmwareVersion: failed to get firmware manifest, %s", err)
}
var manifest FirmwareManifest
if err := json.Unmarshal([]byte(out), &manifest); err != nil {
return "", fmt.Errorf("getAvailableFirmwareVersion: failed to unmarshal firmware manifest, %s", err)
}
fwModel, err := getFirmwareTarget(c)
if err != nil {
return "", fmt.Errorf("getAvailableFirmwareVersion: failed to get firmware target %s", err)
}
if data, ok := manifest[fwModel]; ok {
log.Printf("Available firmware from the new OS: %s.", data.Host.Versions.Rw)
return data.Host.Versions.Rw, nil
}
return "", fmt.Errorf("getAvailableFirmwareVersion: failed to get firmware data of key %s from manifest, %s", fwModel, err)
}
// getFirmwareTarget returns firmware target of the DUT, which will be used to as key to fetch expected firmware from manifest.
func getFirmwareTarget(c *ssh.Client) (string, error) {
out, err := runCmdOutput(c, "crosid")
if err != nil {
return "", err
}
fwLine := firmwareManifestRegexp.FindString(out)
if fwLine != "" {
return strings.TrimLeft(strings.TrimRight(fwLine, "'"), "FIRMWARE_MANIFEST_KEY='"), nil
}
return "", fmt.Errorf("getFirmwareTarget: unable to parse FIRMWARE_MANIFEST_KEY from crosid.")
}
// getCurrentFirmwareVersion read current system firmware version on the DUT.
func getCurrentFirmwareVersion(c *ssh.Client) (string, error) {
out, err := runCmdOutput(c, "crossystem fwid")
if err != nil {
return "", fmt.Errorf("getCurrentFirmwareVersion: failed to read current system firmware, %s", err)
}
log.Printf("Current firmware on DUT: %s.", out)
return out, nil
}
// updateFirmware update DUT's firmware(RW) to current available version from OS image.
func (p *provisionState) updateFirmware(ctx context.Context) (bool, error) {
if err := runCmd(p.c, fmt.Sprintf("%s --wp=1 --mode=autoupdate", firmwareUpdaterPath)); err != nil {
return false, fmt.Errorf("updateFirmware: failed to execute chromeos-firmwareupdate, %s", err)
}
fwChanged, err := isFirmwareSlotChanged(p.c)
if err != nil {
return false, err
}
if p.preventReboot {
log.Printf("updateFirmware: reboot prevented by request")
return fwChanged, nil
}
if fwChanged {
log.Printf("Firmware slot changed on next boot, rebooting the DUT.")
if err := rebootDUT(ctx, p.c); err != nil {
return fwChanged, fmt.Errorf("updateFirmware: failed to reboot DUT, %s", err)
}
}
return fwChanged, nil
}
func isFirmwareSlotChanged(c *ssh.Client) (bool, error) {
current, err := runCmdOutput(c, "crossystem mainfw_act")
if err != nil {
return false, fmt.Errorf("isFirmwareSlotChanged: failed to get current active main firmware slot, %s", err)
}
next, err := runCmdOutput(c, "crossystem fw_try_next")
if err != nil {
return false, fmt.Errorf("isFirmwareSlotChanged: failed to get next main firmware slot, %s", err)
}
log.Printf("Current active firmware slot: %s, next boot firmware slot: %s", current, next)
return current != next, nil
}
func checkFirmwareUpdaterExist(c *ssh.Client) error {
return runCmd(c, fmt.Sprintf("test -f %s", firmwareUpdaterPath))
}