Files
Michael Stapelberg 9c06b898c1 status: display running EEPROM bootloader build-timestamp
This is more user-friendly compared to the raw SHA256 checksums.
Those can still be obtained by querying the status via JSON.

fixes https://github.com/gokrazy/gokrazy/issues/343
2025-11-20 08:13:35 +01:00

105 lines
2.6 KiB
Go

package gokrazy
import (
"encoding/binary"
"fmt"
"io"
"log"
"os"
"strings"
"time"
"github.com/gokrazy/internal/fat"
"github.com/gokrazy/internal/rootdev"
)
type eepromVersion struct {
PieepromSHA256 string // pieeprom.sig
VL805SHA256 string // vl805.sig
FirmwareDate string // build timestamp of running firmware (yyyy-mm-dd)
}
// filled by SuperviseServices()
var lastInstalledEepromVersion *eepromVersion
func readEEPROMVersions(model string) (*eepromVersion, error) {
var versions eepromVersion
if err := readRunningEepromVersion(&versions); err != nil {
log.Printf("(not critical) could not read running EEPROM firmware version: %v", err)
}
if err := readLastInstalledEepromVersion(&versions, model); err != nil {
return nil, err
}
return &versions, nil
}
func readRunningEepromVersion(versions *eepromVersion) error {
b, err := os.ReadFile("/sys/firmware/devicetree/base/chosen/bootloader/build-timestamp")
if err != nil {
return err
}
if len(b) < 4 {
return fmt.Errorf("build-timestamp file unexpectedly < 4 bytes long: %x (%d)", b, len(b))
}
buildUnix := binary.BigEndian.Uint32(b)
buildTime := time.Unix(int64(buildUnix), 0)
versions.FirmwareDate = buildTime.Format("2006-01-02")
return nil
}
func readLastInstalledEepromVersion(versions *eepromVersion, model string) error {
f, err := os.OpenFile(rootdev.Partition(rootdev.Boot), os.O_RDONLY, 0600)
if err != nil {
return err
}
defer f.Close()
rd, err := fat.NewReader(f)
if err != nil {
return err
}
if strings.HasPrefix(model, "Raspberry Pi 4 ") {
// Only the Pi 4 firmware renames recovery.bin to RECOVERY.000,
// the Pi 5 uses the timestamp from the .sig files to run/skip
// an update, but no files are renamed.
if _, err := rd.ModTime("/RECOVERY.000"); err != nil {
return fmt.Errorf("RECOVERY.000 not found, assuming update unsuccessful")
}
}
// Get all extents before we start seeking, which confuses the fat.Reader.
offsetE, lengthE, err := rd.Extents("/pieeprom.sig")
if err != nil {
return err
}
offsetV, lengthV, err := rd.Extents("/vl805.sig")
if err != nil {
if strings.HasPrefix(model, "Raspberry Pi 4 ") {
return err
}
}
{
if _, err := f.Seek(offsetE, io.SeekStart); err != nil {
return err
}
b := make([]byte, lengthE)
if _, err := f.Read(b); err != nil {
return err
}
versions.PieepromSHA256 = strings.TrimSpace(string(b))
}
if offsetV > 0 && lengthV > 0 {
if _, err := f.Seek(offsetV, io.SeekStart); err != nil {
return err
}
b := make([]byte, lengthV)
if _, err := f.Read(b); err != nil {
return err
}
versions.VL805SHA256 = strings.TrimSpace(string(b))
}
return nil
}