mesh/delivery merged: source.main moved to novox/mesh-catalog@e271757db78b
mesh/check checking c100e16ba0af on e271757db78b
intel-ucode failed an apply on a stale package database and stopped the delivery; an Intel machine fails the unit by the image check and gets intel-ucode in the change that adds it. Tests: 1977/AC1 and the pinned order string adjusted. Builds: L10 (issue 1977 note 6)
191 lines
7.7 KiB
Go
191 lines
7.7 KiB
Go
package main
|
|
|
|
// Microcode in every initramfs (novox/hq issue 1977, lead answers L1-L4): the manifest, the drop-in sourced by bash,
|
|
// the initramfs script run with sh over stand-in files and a stand-in mkinitcpio, and kernel_boot's answer.
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// 1977/AC1
|
|
func TestAC1TheManifestDeclaresTheMicrocodePackagesDropInScriptAndService(t *testing.T) {
|
|
m := manifest(t)
|
|
var ids []string
|
|
for _, r := range m.Resources {
|
|
ids = append(ids, r["id"].(string))
|
|
}
|
|
if !strings.HasSuffix(strings.Join(ids, " "), "modules-cleanup amd-ucode microcode-hook initramfs initramfs-unit initramfs-apply") {
|
|
t.Fatalf("order: %v", ids)
|
|
}
|
|
for id, pkg := range map[string]string{"amd-ucode": "amd-ucode"} {
|
|
if r := m.resource(t, id); r["type"] != "package" || r["package"] != pkg {
|
|
t.Fatalf("%s: %v", id, r)
|
|
}
|
|
}
|
|
if r := m.resource(t, "microcode-hook"); r["path"] != "/etc/mkinitcpio.conf.d/mesh-microcode.conf" {
|
|
t.Fatalf("drop-in: %v", r)
|
|
}
|
|
if r := m.resource(t, "initramfs"); r["path"] != "${dir:state}/initramfs" || r["mode"] != "0755" || r["trusted"] != true {
|
|
t.Fatalf("script: %v", r)
|
|
}
|
|
if r := m.resource(t, "initramfs-unit"); r["path"] != "/etc/systemd/system/mesh-kernel-initramfs.service" {
|
|
t.Fatalf("unit: %v", r)
|
|
}
|
|
a := m.resource(t, "initramfs-apply")
|
|
on := a["restart-on"].([]any)
|
|
if a["unit"] != "mesh-kernel-initramfs.service" || a["state"] != "running" || a["boot"] != "enabled" ||
|
|
len(on) != 3 || on[0] != "initramfs-unit" || on[1] != "initramfs" || on[2] != "microcode-hook" {
|
|
t.Fatalf("service: %v", a)
|
|
}
|
|
}
|
|
|
|
// 1977/AC2
|
|
func TestAC2TheDropInAddsMicrocodeToHooksOnce(t *testing.T) {
|
|
drop := filepath.Join(t.TempDir(), "mesh-microcode.conf")
|
|
write(t, drop, manifest(t).resource(t, "microcode-hook").text(t), 0o644)
|
|
for _, c := range []struct{ hooks, want string }{
|
|
{"base udev autodetect modconf block filesystems keyboard fsck", "base udev autodetect modconf block filesystems keyboard fsck microcode"},
|
|
{"base udev autodetect microcode block", "base udev autodetect microcode block"},
|
|
} {
|
|
out, err := exec.Command("bash", "-c", "HOOKS=("+c.hooks+"); . "+drop+"; echo \"${HOOKS[*]}\"").CombinedOutput()
|
|
if err != nil || strings.TrimSpace(string(out)) != c.want {
|
|
t.Errorf("%q: %v %q", c.hooks, err, out)
|
|
}
|
|
}
|
|
}
|
|
|
|
type initBed struct{ script, dropin, state, runs, bin, presets, boot string }
|
|
|
|
func newInitBed(t *testing.T, mkinitcpioExit string) initBed {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
b := initBed{script: filepath.Join(dir, "initramfs"), dropin: filepath.Join(dir, "mesh-microcode.conf"), state: filepath.Join(dir, "state"),
|
|
runs: filepath.Join(dir, "runs"), bin: filepath.Join(dir, "bin"), presets: filepath.Join(dir, "presets"), boot: filepath.Join(dir, "boot")}
|
|
for _, d := range []string{b.state, b.bin, b.presets, b.boot} {
|
|
if err := os.MkdirAll(d, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
content := strings.ReplaceAll(manifest(t).resource(t, "initramfs").text(t), "${dir:state}", b.state)
|
|
if strings.Contains(content, "${") {
|
|
t.Fatal("a substitution is left in the script")
|
|
}
|
|
write(t, b.script, content, 0o755)
|
|
write(t, b.dropin, "HOOKS+=(microcode)\n", 0o644)
|
|
// the stand-in mkinitcpio writes an image whose early archive holds microcode.
|
|
b.mkinitcpio(t, "kernel/x86/microcode/AuthenticAMD.bin", mkinitcpioExit)
|
|
return b
|
|
}
|
|
|
|
func (b initBed) mkinitcpio(t *testing.T, early, exit string) {
|
|
write(t, filepath.Join(b.bin, "mkinitcpio"), "#!/bin/sh\necho \"$@\" >>"+b.runs+"\nprintf '070701%s\\0' "+early+" >"+b.boot+"/initramfs-linux.img\nexit "+exit+"\n", 0o755)
|
|
}
|
|
|
|
func (b initBed) run(t *testing.T) (string, error) {
|
|
cmd := exec.Command("sh", b.script, "apply", b.dropin, b.state, b.presets, b.boot)
|
|
cmd.Env = append(os.Environ(), "PATH="+b.bin+string(os.PathListSeparator)+os.Getenv("PATH"))
|
|
out, err := cmd.CombinedOutput()
|
|
return string(out), err
|
|
}
|
|
|
|
func (b initBed) builds(t *testing.T) string {
|
|
raw, _ := os.ReadFile(b.runs)
|
|
return string(raw)
|
|
}
|
|
|
|
// 1977/AC3
|
|
func TestAC3TheImagesAreBuiltOnceAndAgainWhenTheDropInChanges(t *testing.T) {
|
|
b := newInitBed(t, "0")
|
|
if out, err := b.run(t); err != nil || b.builds(t) != "-P\n" {
|
|
t.Fatalf("first: %v %s %q", err, out, b.builds(t))
|
|
}
|
|
if raw, _ := os.ReadFile(filepath.Join(b.state, "initramfs-built")); string(raw) != "HOOKS+=(microcode)\n" {
|
|
t.Fatalf("recorded: %q", raw)
|
|
}
|
|
if out, err := b.run(t); err != nil || !strings.Contains(out, "as it was") || b.builds(t) != "-P\n" {
|
|
t.Fatalf("again: %v %s %q", err, out, b.builds(t))
|
|
}
|
|
write(t, b.dropin, "HOOKS+=(microcode)\n# new\n", 0o644)
|
|
if _, err := b.run(t); err != nil || b.builds(t) != "-P\n-P\n" {
|
|
t.Fatalf("changed: %v %q", err, b.builds(t))
|
|
}
|
|
}
|
|
|
|
// 1977/AC4
|
|
func TestAC4AFailedMkinitcpioFailsTheUnitAndRecordsNothing(t *testing.T) {
|
|
b := newInitBed(t, "1")
|
|
out, err := b.run(t)
|
|
if err == nil || !strings.Contains(out, "mkinitcpio failed") {
|
|
t.Fatalf("%v %s", err, out)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(b.state, "initramfs-built")); !os.IsNotExist(err) {
|
|
t.Fatalf("recorded after a failure: %v", err)
|
|
}
|
|
}
|
|
|
|
// 1977/AC5
|
|
func TestAC5KernelBootNamesTheRunningMicrocodeAndWhichImagesCarryIt(t *testing.T) {
|
|
root := t.TempDir()
|
|
put(t, root, "proc/cpuinfo", "processor\t: 0\nmicrocode\t: 0x0a201016\nmodel name\t: x\n")
|
|
// a stand-in early cpio: a name field, then padding, as the first member of an initramfs.
|
|
put(t, root, "boot/initramfs-linux.img", "070701000000kernel/x86/microcode/AuthenticAMD.bin\x00\x00"+strings.Repeat("z", 100))
|
|
put(t, root, "boot/initramfs-linux-lts.img", "070701000000early/other.bin\x00"+strings.Repeat("z", 100))
|
|
got := bootOf(t, root)
|
|
if got.MicrocodeRevision != "0x0a201016" || len(got.ImagesWithMicrocode) != 2 ||
|
|
!got.ImagesWithMicrocode["initramfs-linux.img"] || got.ImagesWithMicrocode["initramfs-linux-lts.img"] || len(got.ImageErrors) != 0 {
|
|
t.Fatalf("%+v", got)
|
|
}
|
|
if os.Getuid() == 0 {
|
|
t.Skip("root reads an unreadable file")
|
|
}
|
|
bad := filepath.Join(root, "boot/initramfs-linux-fallback.img")
|
|
put(t, root, "boot/initramfs-linux-fallback.img", "x")
|
|
if err := os.Chmod(bad, 0); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got = bootOf(t, root)
|
|
if _, in := got.ImagesWithMicrocode["initramfs-linux-fallback.img"]; in || len(got.ImageErrors) != 1 ||
|
|
got.ImageErrors[0].Image != "initramfs-linux-fallback.img" || got.ImageErrors[0].Error == "" {
|
|
t.Fatalf("unreadable: %+v", got)
|
|
}
|
|
}
|
|
|
|
// 1977/AC7
|
|
func TestAC7ADefaultConfigPresetIsTakenOverAndAnotherIsLeftAndNamed(t *testing.T) {
|
|
b := newInitBed(t, "0")
|
|
preset := "ALL_kver=\"/boot/vmlinuz-linux\"\nALL_config=\"/etc/mkinitcpio.conf\"\nPRESETS=('default')\n"
|
|
write(t, filepath.Join(b.presets, "linux.preset"), preset, 0o644)
|
|
other := "ALL_config=\"/etc/other.conf\"\nPRESETS=('default')\n"
|
|
write(t, filepath.Join(b.presets, "linux-lts.preset"), other, 0o644)
|
|
out, err := b.run(t)
|
|
if err != nil {
|
|
t.Fatalf("%v %s", err, out)
|
|
}
|
|
if got, _ := os.ReadFile(filepath.Join(b.presets, "linux.preset")); string(got) != "ALL_kver=\"/boot/vmlinuz-linux\"\nPRESETS=('default')\n" {
|
|
t.Fatalf("preset: %q", got)
|
|
}
|
|
if got, _ := os.ReadFile(filepath.Join(b.state, "taken-over", "linux.preset")); string(got) != preset {
|
|
t.Fatalf("copy: %q", got)
|
|
}
|
|
if got, _ := os.ReadFile(filepath.Join(b.presets, "linux-lts.preset")); string(got) != other || !strings.Contains(out, "linux-lts.preset sets ALL_config=\"/etc/other.conf\"") {
|
|
t.Fatalf("other: %q %s", got, out)
|
|
}
|
|
}
|
|
|
|
// 1977/AC8
|
|
func TestAC8AnImageWithoutMicrocodeFailsTheUnitNamesItAndRecordsNothing(t *testing.T) {
|
|
b := newInitBed(t, "0")
|
|
b.mkinitcpio(t, "early/other.bin", "0")
|
|
out, err := b.run(t)
|
|
if err == nil || !strings.Contains(out, "initramfs-linux.img") {
|
|
t.Fatalf("%v %s", err, out)
|
|
}
|
|
if _, err := os.Stat(filepath.Join(b.state, "initramfs-built")); !os.IsNotExist(err) {
|
|
t.Fatalf("recorded: %v", err)
|
|
}
|
|
}
|