Use microcode as CPU firmware
This commit is contained in:
@@ -8,6 +8,14 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var execDmidecode = func(typeNum string) (string, error) {
|
||||||
|
out, err := exec.Command("dmidecode", "-t", typeNum).Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(out), nil
|
||||||
|
}
|
||||||
|
|
||||||
// collectBoard runs dmidecode for types 0, 1, 2 and returns the board record
|
// collectBoard runs dmidecode for types 0, 1, 2 and returns the board record
|
||||||
// plus the BIOS firmware entry. Any failure is logged and returns zero values.
|
// plus the BIOS firmware entry. Any failure is logged and returns zero values.
|
||||||
func collectBoard() (schema.HardwareBoard, []schema.HardwareFirmwareRecord) {
|
func collectBoard() (schema.HardwareBoard, []schema.HardwareFirmwareRecord) {
|
||||||
@@ -141,9 +149,5 @@ func cleanDMIValue(v string) string {
|
|||||||
|
|
||||||
// runDmidecode executes dmidecode -t <typeNum> and returns its stdout.
|
// runDmidecode executes dmidecode -t <typeNum> and returns its stdout.
|
||||||
func runDmidecode(typeNum string) (string, error) {
|
func runDmidecode(typeNum string) (string, error) {
|
||||||
out, err := exec.Command("dmidecode", "-t", typeNum).Output()
|
return execDmidecode(typeNum)
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(out), nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,9 +24,7 @@ func Run(_ runtimeenv.Mode) schema.HardwareIngestRequest {
|
|||||||
snap.Board = board
|
snap.Board = board
|
||||||
snap.Firmware = append(snap.Firmware, biosFW...)
|
snap.Firmware = append(snap.Firmware, biosFW...)
|
||||||
|
|
||||||
cpus, cpuFW := collectCPUs(snap.Board.SerialNumber)
|
snap.CPUs = collectCPUs(snap.Board.SerialNumber)
|
||||||
snap.CPUs = cpus
|
|
||||||
snap.Firmware = append(snap.Firmware, cpuFW...)
|
|
||||||
|
|
||||||
snap.Memory = collectMemory()
|
snap.Memory = collectMemory()
|
||||||
sensorDoc, err := readSensorsJSONDoc()
|
sensorDoc, err := readSensorsJSONDoc()
|
||||||
|
|||||||
@@ -6,30 +6,28 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// collectCPUs runs dmidecode -t 4 and reads microcode version from sysfs.
|
// collectCPUs runs dmidecode -t 4 and enriches CPUs with microcode from sysfs.
|
||||||
func collectCPUs(boardSerial string) ([]schema.HardwareCPU, []schema.HardwareFirmwareRecord) {
|
func collectCPUs(boardSerial string) []schema.HardwareCPU {
|
||||||
out, err := runDmidecode("4")
|
out, err := runDmidecode("4")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Warn("cpu: dmidecode type 4 failed", "err", err)
|
slog.Warn("cpu: dmidecode type 4 failed", "err", err)
|
||||||
return nil, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cpus := parseCPUs(out, boardSerial)
|
cpus := parseCPUs(out, boardSerial)
|
||||||
|
|
||||||
var firmware []schema.HardwareFirmwareRecord
|
|
||||||
if mc := readMicrocode(); mc != "" {
|
if mc := readMicrocode(); mc != "" {
|
||||||
firmware = append(firmware, schema.HardwareFirmwareRecord{
|
for i := range cpus {
|
||||||
DeviceName: "CPU Microcode",
|
cpus[i].Firmware = &mc
|
||||||
Version: mc,
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
slog.Info("cpu: collected", "count", len(cpus))
|
slog.Info("cpu: collected", "count", len(cpus))
|
||||||
return cpus, firmware
|
return cpus
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseCPUs splits dmidecode output into per-processor sections and parses each.
|
// parseCPUs splits dmidecode output into per-processor sections and parses each.
|
||||||
@@ -180,7 +178,7 @@ func parseInt(v string) int {
|
|||||||
// readMicrocode reads the CPU microcode revision from sysfs.
|
// readMicrocode reads the CPU microcode revision from sysfs.
|
||||||
// Returns empty string if unavailable.
|
// Returns empty string if unavailable.
|
||||||
func readMicrocode() string {
|
func readMicrocode() string {
|
||||||
data, err := os.ReadFile("/sys/devices/system/cpu/cpu0/microcode/version")
|
data, err := os.ReadFile(filepath.Join(cpuSysBaseDir, "cpu0", "microcode", "version"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package collector
|
package collector
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -63,6 +65,39 @@ func TestParseCPUs_unpopulated_skipped(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCollectCPUsSetsFirmwareFromMicrocode(t *testing.T) {
|
||||||
|
tmp := t.TempDir()
|
||||||
|
origBase := cpuSysBaseDir
|
||||||
|
cpuSysBaseDir = tmp
|
||||||
|
t.Cleanup(func() { cpuSysBaseDir = origBase })
|
||||||
|
|
||||||
|
if err := os.MkdirAll(filepath.Join(tmp, "cpu0", "microcode"), 0755); err != nil {
|
||||||
|
t.Fatalf("mkdir microcode dir: %v", err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(tmp, "cpu0", "microcode", "version"), []byte("0x2b000643\n"), 0644); err != nil {
|
||||||
|
t.Fatalf("write microcode version: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
origRun := execDmidecode
|
||||||
|
execDmidecode = func(typeNum string) (string, error) {
|
||||||
|
if typeNum != "4" {
|
||||||
|
t.Fatalf("unexpected dmidecode type: %s", typeNum)
|
||||||
|
}
|
||||||
|
return mustReadFile(t, "testdata/dmidecode_type4.txt"), nil
|
||||||
|
}
|
||||||
|
t.Cleanup(func() { execDmidecode = origRun })
|
||||||
|
|
||||||
|
cpus := collectCPUs("CAR315KA0803B90")
|
||||||
|
if len(cpus) != 2 {
|
||||||
|
t.Fatalf("expected 2 CPUs, got %d", len(cpus))
|
||||||
|
}
|
||||||
|
for i, cpu := range cpus {
|
||||||
|
if cpu.Firmware == nil || *cpu.Firmware != "0x2b000643" {
|
||||||
|
t.Fatalf("cpu[%d] firmware=%v want microcode", i, cpu.Firmware)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseCPUStatus(t *testing.T) {
|
func TestParseCPUStatus(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
|
|||||||
Reference in New Issue
Block a user