package main // kernel_boot: which loader booted the machine, its entries, the kernels in /boot, the entry it booted and // whether the fallback kernel is on the menu (novox/hq ADR 0508 ยง9, issue 1841). Reads only. import ( "os" "path/filepath" "regexp" "sort" "strings" "syscall" "unicode/utf16" ) const loaderSelected = "sys/firmware/efi/efivars/LoaderEntrySelected-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f" // BootState is what kernel_boot answers. type BootState struct { Loader string `json:"loader"` Entries []string `json:"entries"` Kernels []string `json:"kernels"` Booted string `json:"booted"` FallbackPresent bool `json:"fallback_present"` 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"` } // 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" 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. func (m *Machine) microcode(b *BootState) { 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 } var grubKernel = regexp.MustCompile(`(?m)^\s*linux\s+(?:/boot)?/(vmlinuz-\S+)`) var grubTitle = regexp.MustCompile(`(?m)^\s*menuentry\s+'([^']*)'`) var bootImage = regexp.MustCompile(`BOOT_IMAGE=(?:/boot)?/?(\S+)`) // Boot reads the machine, the loader the way the boot-entries script does: the loader's EFI variable means // systemd-boot, a grub.cfg without it GRUB. func (m *Machine) Boot() (*BootState, error) { b := &BootState{Loader: "none", Entries: []string{}, Kernels: []string{}} m.microcode(b) files, _ := filepath.Glob(m.path("boot/vmlinuz-*")) for _, f := range files { b.Kernels = append(b.Kernels, filepath.Base(f)) } sort.Strings(b.Kernels) hasKernel := func(name string) bool { for _, k := range b.Kernels { if k == name { return true } } return false } var s syscall.Statfs_t if err := syscall.Statfs(m.path("boot"), &s); err == nil { b.BootFreeKiB = int64(s.Bavail) * int64(s.Bsize) / 1024 } if raw, err := os.ReadFile(m.path(loaderSelected)); err == nil { b.Loader = "systemd-boot" if len(raw) > 4 { var u []uint16 for i := 4; i+1 < len(raw); i += 2 { u = append(u, uint16(raw[i])|uint16(raw[i+1])<<8) } b.Booted = strings.TrimRight(string(utf16.Decode(u)), "\x00") } files, _ := filepath.Glob(m.path("boot/loader/entries/*.conf")) for _, f := range files { b.Entries = append(b.Entries, filepath.Base(f)) } sort.Strings(b.Entries) if raw, err := os.ReadFile(m.path("boot/loader/entries/mesh-linux-lts.conf")); err == nil { b.FallbackPresent = strings.Contains(string(raw), "/vmlinuz-linux-lts") && hasKernel("vmlinuz-linux-lts") } return b, nil } if raw, err := os.ReadFile(m.path("boot/grub/grub.cfg")); err == nil { b.Loader = "grub" for _, t := range grubTitle.FindAllStringSubmatch(string(raw), -1) { b.Entries = append(b.Entries, t[1]) } for _, k := range grubKernel.FindAllStringSubmatch(string(raw), -1) { if k[1] == "vmlinuz-linux-lts" && hasKernel("vmlinuz-linux-lts") { b.FallbackPresent = true } } if cmd, err := m.read("proc/cmdline"); err == nil { if i := bootImage.FindStringSubmatch(cmd); i != nil { b.Booted = i[1] } } } return b, nil }