diff --git a/docs/manpage.md b/docs/manpage.md index e498dbfa..8807762b 100644 --- a/docs/manpage.md +++ b/docs/manpage.md @@ -71,6 +71,8 @@ Booster advantages: * `enable_zfs` is a flag that enables ZFS filesystem as root filesystem. This flag also makes sure all the required modules/binaries are added to the image. Note that if ZFS is enabled then `zfs=` boot option must be used instead of `root=` boot option. + * `enable_microcode` is a flag makes booster generator detect microcode file for the current CPU, add it to image and later load it the early boot stages. + Once you are done modifying your config file and want to regenerate booster images under `/boot` please use `/usr/lib/booster/regenerate_images`. It is a convenience script that performs the same type of image regeneration as if you installed `booster` with your package manager. diff --git a/generator/config.go b/generator/config.go index b97ca9d0..9667a022 100644 --- a/generator/config.go +++ b/generator/config.go @@ -38,6 +38,7 @@ type UserConfig struct { EnableZfs bool `yaml:"enable_zfs"` ZfsImportParams string `yaml:"zfs_import_params"` ZfsCachePath string `yaml:"zfs_cache_path"` + EnableMicrocode bool `yaml:"enable_zfs"` } // read user config from the specified file. If file parameter is empty string then "empty" configuration is considered @@ -149,6 +150,7 @@ func readGeneratorConfig(file string) (*generatorConfig, error) { conf.enableZfs = u.EnableZfs conf.zfsImportParams = u.ZfsImportParams conf.zfsCachePath = u.ZfsCachePath + conf.enableMicrocode = u.EnableMicrocode conf.enableVirtualConsole = u.EnableVirtualConsole if conf.enableVirtualConsole { conf.vconsolePath = "/etc/vconsole.conf" diff --git a/generator/generator.go b/generator/generator.go index 05cf6c94..76948a50 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -10,6 +10,7 @@ import ( "time" "github.com/cavaliergopher/cpio" + "github.com/klauspost/cpuid/v2" "gopkg.in/yaml.v3" ) @@ -45,6 +46,7 @@ type generatorConfig struct { // virtual console configs enableVirtualConsole bool vconsolePath, localePath string + enableMicrocode bool } type networkStaticConfig struct { @@ -64,6 +66,9 @@ const ( var ( imageModulesDir = "/usr/lib/modules/" firmwareDir = "/usr/lib/firmware/" + + intelUcodeDir = "/lib/firmware/intel-ucode" + amdUcodeDir = "/usr/lib/firmware/amd-ucode" ) // This is default modules list checked by booster. It either specifies a name of the module @@ -228,6 +233,43 @@ func generateInitRamfs(conf *generatorConfig) error { } } + if conf.enableMicrocode { + if conf.universal { + // we detect microcode file for specific host/platform. This logic does not work with universal images currently. + return fmt.Errorf("microcode detection does not work with universal images") + } + + infile := "" + outfile := "" + vendor := cpuid.CPU.VendorID + switch vendor { + case cpuid.Intel: + family := cpuid.CPU.Family + model := cpuid.CPU.Model + stepping := 10 // cpuid.CPU.Stepping + infile = fmt.Sprintf("%s/%02x-%02x-%02x", intelUcodeDir, family, model, stepping) + outfile = "GenuineIntel.bin" + case cpuid.AMD: + family := cpuid.CPU.Family + if family >= 21 { + infile = fmt.Sprintf("%s/microcode_amd_fam%xh.bin", amdUcodeDir, family) + } else { + infile = fmt.Sprintf("%s/microcode_amd.bin", amdUcodeDir) + } + outfile = "AuthenticAMD.bin" + default: + return fmt.Errorf("unable to find microcode for processor vendor %s", vendor.String()) + } + + content, err := os.ReadFile(infile) + if err != nil { + return err + } + if err := img.AppendContent("/kernel/x86/microcode/"+outfile, 0644, content); err != nil { + return err + } + } + if err := img.appendInitConfig(conf, kmod, vconsole); err != nil { return err } diff --git a/generator/generator_test.go b/generator/generator_test.go index a07317bb..d19d13a9 100644 --- a/generator/generator_test.go +++ b/generator/generator_test.go @@ -65,6 +65,7 @@ type options struct { vConsoleConfig, localeConfig string enableMdraid bool mdraidConfigPath string + microcode bool } func generateAliasesFile(aliases []alias) []byte { @@ -179,6 +180,7 @@ func createTestInitRamfs(t *testing.T, o *options) { enableLVM: o.enableLVM, enableMdraid: o.enableMdraid, mdraidConfigPath: o.mdraidConfigPath, + enableMicrocode: o.microcode, } if o.vConsoleConfig != "" { conf.enableVirtualConsole = true @@ -554,3 +556,28 @@ func TestModprobeOptions(t *testing.T) { } require.Equal(t, expect, cfg.ModprobeOptions) } + +func TestMicrocode(t *testing.T) { + // both Intel and AMD ucode dirs must exist to be able to run this test + if _, err := os.Stat("/lib/firmware/intel-ucode"); err != nil { + t.Skip(err) + } + if _, err := os.Stat("/lib/firmware/amd-ucode"); err != nil { + t.Skip(err) + } + + opts := options{ + microcode: true, + unpackImage: true, + } + createTestInitRamfs(t, &opts) + + dir := opts.workDir + "/image.unpacked/kernel/x86/microcode/" + checkFileExistence(t, dir) + + entries, err := os.ReadDir(dir) + require.NoError(t, err) + require.Len(t, entries, 1) + name := entries[0].Name() + require.True(t, name == "GenuineIntel.bin" || name == "AuthenticAMD.bin") +} diff --git a/go.mod b/go.mod index a9126c5b..e7e911f9 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f github.com/jessevdk/go-flags v1.5.0 github.com/klauspost/compress v1.15.8 + github.com/klauspost/cpuid/v2 v2.1.0 github.com/stretchr/testify v1.8.0 github.com/ulikunitz/xz v0.5.10 github.com/vishvananda/netlink v1.1.0 diff --git a/go.sum b/go.sum index 9de4e355..649b6a5c 100644 --- a/go.sum +++ b/go.sum @@ -107,6 +107,8 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.15.8 h1:JahtItbkWjf2jzm/T+qgMxkP9EMHsqEUA6vCMGmXvhA= github.com/klauspost/compress v1.15.8/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/cpuid/v2 v2.1.0 h1:eyi1Ad2aNJMW95zcSbmGg7Cg6cq3ADwLpMAP96d8rF0= +github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= @@ -267,6 +269,7 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e h1:NHvCuwuS43lGnYhten69ZWqi2QOj/CiDNcKbVqwVoew= golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=