mirror of
https://github.com/siderolabs/talos.git
synced 2025-10-26 14:01:39 +01:00
Closes #7729 This follows the steps described in https://www.kernel.org/doc/html/v6.1/x86/microcode.html#early-load-microcode Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
192 lines
4.5 KiB
Go
192 lines
4.5 KiB
Go
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package extensions
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
|
|
"github.com/siderolabs/talos/pkg/machinery/constants"
|
|
)
|
|
|
|
// List of globs and destinations for early CPU ucode.
|
|
//
|
|
// See https://www.kernel.org/doc/html/v6.1/x86/microcode.html#early-load-microcode.
|
|
//
|
|
// We need to repackage the ucode blobs matching the glob into the destination concatenating
|
|
// them all together.
|
|
// The resulting blobs should be placed into uncompressed cpio archive prepended to the normal (compressed) initramfs.
|
|
var earlyCPUUcode = []struct {
|
|
glob, dst string
|
|
}{
|
|
{"/lib/firmware/intel-ucode/*", "kernel/x86/microcode/GenuineIntel.bin"},
|
|
{"/lib/firmware/amd-ucode/microcode_amd*.bin", "kernel/x86/microcode/AuthenticAMD.bin"},
|
|
}
|
|
|
|
// List of paths to be moved to the future initramfs.
|
|
var initramfsPaths = []string{
|
|
constants.FirmwarePath,
|
|
}
|
|
|
|
// Compress builds the squashfs image in the specified destination folder.
|
|
//
|
|
// Components which should be placed to the initramfs are moved to the initramfsPath.
|
|
// Ucode components are moved into a separate designated location.
|
|
func (ext *Extension) Compress(squashPath, initramfsPath string) (string, error) {
|
|
if err := ext.handleUcode(initramfsPath); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
for _, path := range initramfsPaths {
|
|
if _, err := os.Stat(filepath.Join(ext.rootfsPath, path)); err == nil {
|
|
if err = moveFiles(filepath.Join(ext.rootfsPath, path), filepath.Join(initramfsPath, path)); err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
}
|
|
|
|
squashPath = filepath.Join(squashPath, fmt.Sprintf("%s.sqsh", ext.directory))
|
|
|
|
cmd := exec.Command("mksquashfs", ext.rootfsPath, squashPath, "-all-root", "-noappend", "-comp", "xz", "-Xdict-size", "100%", "-no-progress")
|
|
cmd.Stderr = os.Stderr
|
|
|
|
return squashPath, cmd.Run()
|
|
}
|
|
|
|
func appendBlob(dst io.Writer, srcPath string) error {
|
|
src, err := os.Open(srcPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer src.Close() //nolint:errcheck
|
|
|
|
if _, err = io.Copy(dst, src); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = src.Close(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return os.Remove(srcPath)
|
|
}
|
|
|
|
func (ext *Extension) handleUcode(initramfsPath string) error {
|
|
for _, ucode := range earlyCPUUcode {
|
|
matches, err := filepath.Glob(filepath.Join(ext.rootfsPath, ucode.glob))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(matches) == 0 {
|
|
continue
|
|
}
|
|
|
|
// if some ucode is found, append it to the blob in the initramfs
|
|
if err = os.MkdirAll(filepath.Dir(filepath.Join(initramfsPath, ucode.dst)), 0o755); err != nil {
|
|
return err
|
|
}
|
|
|
|
dst, err := os.OpenFile(filepath.Join(initramfsPath, ucode.dst), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer dst.Close() //nolint:errcheck
|
|
|
|
for _, match := range matches {
|
|
if err = appendBlob(dst, match); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err = dst.Close(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func moveFiles(srcPath, dstPath string) error {
|
|
return handleFilesOp(srcPath, dstPath, os.Remove)
|
|
}
|
|
|
|
func copyFiles(srcPath, dstPath string) error {
|
|
return handleFilesOp(srcPath, dstPath, nil)
|
|
}
|
|
|
|
func handleFilesOp(srcPath, dstPath string, op func(string) error) error {
|
|
st, err := os.Stat(srcPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if st.IsDir() {
|
|
return handleDirectoryOp(st, srcPath, dstPath, op)
|
|
}
|
|
|
|
return handleFileOp(st, srcPath, dstPath, op)
|
|
}
|
|
|
|
func moveFile(st fs.FileInfo, srcPath, dstPath string) error {
|
|
return handleFileOp(st, srcPath, dstPath, os.Remove)
|
|
}
|
|
|
|
func handleFileOp(st fs.FileInfo, srcPath, dstPath string, op func(string) error) error {
|
|
src, err := os.Open(srcPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer src.Close() //nolint:errcheck
|
|
|
|
dst, err := os.OpenFile(dstPath, os.O_CREATE|os.O_WRONLY, st.Mode().Perm())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer dst.Close() //nolint:errcheck
|
|
|
|
_, err = io.Copy(dst, src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if op != nil {
|
|
return op(srcPath)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func handleDirectoryOp(st fs.FileInfo, srcPath, dstPath string, op func(string) error) error {
|
|
if err := os.MkdirAll(dstPath, st.Mode().Perm()); err != nil {
|
|
return err
|
|
}
|
|
|
|
contents, err := os.ReadDir(srcPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, item := range contents {
|
|
if err = handleFilesOp(filepath.Join(srcPath, item.Name()), filepath.Join(dstPath, item.Name()), op); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if op != nil {
|
|
return op(srcPath)
|
|
}
|
|
|
|
return nil
|
|
}
|