kernel: kernel_boot reads microcode from each initramfs image (issue 1977, L6)

Replaces the state record read (root's 0700) with images_with_microcode per /boot/initramfs-*.img:
true when the first 64 KiB hold a path under kernel/x86/microcode/; an unreadable image goes to
image_errors with its error. Test: 1977/AC5.

Builds: L6 (issue 1977 note 3)
This commit is contained in:
2026-11-12 20:54:34 +01:00
parent 5795f4d230
commit 62c0eb90cb
4 changed files with 57 additions and 22 deletions
+33 -11
View File
@@ -4,6 +4,8 @@ package main
// whether the fallback kernel is on the menu (novox/hq ADR 0508 §9, issue 1841). Reads only.
import (
"bytes"
"io"
"os"
"path/filepath"
"regexp"
@@ -25,28 +27,48 @@ type BootState struct {
BootFreeKiB int64 `json:"boot_free_kib"`
// MicrocodeRevision is the revision the CPU runs, the first "microcode" line of /proc/cpuinfo ("" when it has none).
MicrocodeRevision string `json:"microcode_revision"`
// ImagesBuiltWithMicrocode is true when the initramfs images were last built with the microcode drop-in as it is now
// (novox/hq issue 1977).
ImagesBuiltWithMicrocode bool `json:"images_built_with_microcode"`
// ImagesWithMicrocode is, per /boot/initramfs-*.img, whether the image's early uncompressed cpio (its first 64 KiB)
// holds a path under kernel/x86/microcode/ (novox/hq issue 1977).
ImagesWithMicrocode map[string]bool `json:"images_with_microcode"`
// ImageErrors names each image that could not be read and why: such an image is in no verdict above.
ImageErrors []ImageError `json:"image_errors"`
}
// stateDir is the module's own directory on the machine, where the initramfs script records the drop-in it built with.
const stateDir = "var/lib/mesh-kernel"
const microcodeDropIn = "etc/mkinitcpio.conf.d/mesh-microcode.conf"
// ImageError is an initramfs image that could not be read.
type ImageError struct {
Image string `json:"image"`
Error string `json:"error"`
}
var cpuMicrocode = regexp.MustCompile(`(?m)^microcode\s*:\s*(\S+)`)
// microcode reads the revision the CPU runs and whether the images carry the drop-in as it is now.
// microcode reads the revision the CPU runs and which initramfs images carry microcode.
func (m *Machine) microcode(b *BootState) {
b.ImagesWithMicrocode = map[string]bool{}
b.ImageErrors = []ImageError{}
if raw, err := m.read("proc/cpuinfo"); err == nil {
if r := cpuMicrocode.FindStringSubmatch(raw); r != nil {
b.MicrocodeRevision = r[1]
}
}
built, err := m.read(stateDir + "/initramfs-built")
now, err2 := m.read(microcodeDropIn)
b.ImagesBuiltWithMicrocode = err == nil && err2 == nil && built == now
images, _ := filepath.Glob(m.path("boot/initramfs-*.img"))
sort.Strings(images)
for _, img := range images {
name := filepath.Base(img)
f, err := os.Open(img)
if err != nil {
b.ImageErrors = append(b.ImageErrors, ImageError{name, err.Error()})
continue
}
head := make([]byte, 64*1024)
n, err := io.ReadFull(f, head)
f.Close()
if err != nil && err != io.ErrUnexpectedEOF && err != io.EOF {
b.ImageErrors = append(b.ImageErrors, ImageError{name, err.Error()})
continue
}
b.ImagesWithMicrocode[name] = bytes.Contains(head[:n], []byte("kernel/x86/microcode/"))
}
}
var grubKernel = regexp.MustCompile(`(?m)^\s*linux\s+(?:/boot)?/(vmlinuz-\S+)`)
+1 -1
View File
@@ -52,7 +52,7 @@ func tools(m *Machine) []stdio.Tool {
"(systemd-boot, read from the EFI variable it sets, grub, or none), the menu's entries, the kernels in /boot, the entry " +
"the machine booted (systemd-boot) or the kernel image it booted (grub), whether the fallback kernel linux-lts is on " +
"the menu with its kernel in /boot, and the room free on /boot in KiB (the fallback kernel needs about 72 MB), the microcode revision the CPU runs " +
"(/proc/cpuinfo) and whether the initramfs images were built with the microcode drop-in (novox/hq issue 1977). " +
"(/proc/cpuinfo) and, per /boot/initramfs-*.img, whether its early cpio carries microcode (images_with_microcode; an unreadable image is named in image_errors) (novox/hq issue 1977). " +
"Replaces ls /boot, bootctl list, reading grub.cfg and df /boot. (r)",
Input: schema(map[string]any{}),
Run: func(args map[string]any) (any, error) { return m.Boot() },
@@ -123,15 +123,28 @@ func TestAC4AFailedMkinitcpioFailsTheUnitAndRecordsNothing(t *testing.T) {
}
// 1977/AC5
func TestAC5KernelBootNamesTheRunningMicrocodeAndWhetherTheImagesCarryTheDropIn(t *testing.T) {
func TestAC5KernelBootNamesTheRunningMicrocodeAndWhichImagesCarryIt(t *testing.T) {
root := t.TempDir()
put(t, root, "proc/cpuinfo", "processor\t: 0\nmicrocode\t: 0x0a201016\nmodel name\t: x\n")
put(t, root, "etc/mkinitcpio.conf.d/mesh-microcode.conf", "HOOKS+=(microcode)\n")
if got := bootOf(t, root); got.MicrocodeRevision != "0x0a201016" || got.ImagesBuiltWithMicrocode {
t.Fatalf("no record: %+v", got)
// 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)
}
put(t, root, "var/lib/mesh-kernel/initramfs-built", "HOOKS+=(microcode)\n")
if got := bootOf(t, root); got.MicrocodeRevision != "0x0a201016" || !got.ImagesBuiltWithMicrocode {
t.Fatalf("recorded: %+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)
}
}