Skip to content

Commit

Permalink
Install grub.cfg into EFI System Partition (#1904)
Browse files Browse the repository at this point in the history
* Install grub.cfg into EFI System Partition

Change the bootloader install logic to install the grub.cfg into the EFI
System Partition (ESP).

This needs some changes to how root is set in the grub.cfg as well as
moving grub_oem_env file to ESP when created in install/reset/upgrade
command.

Install grub-modules to both EFI/BOOT and EFI/ELEMENTAL.

Signed-off-by: Fredrik Lönnegren <[email protected]>
  • Loading branch information
frelon authored Jan 22, 2024
1 parent 2e18efa commit 56b2723
Show file tree
Hide file tree
Showing 24 changed files with 192 additions and 131 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/build_and_test_x86.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ jobs:
sudo rm -rf /usr/local/lib/android # will release about 10 GB if you don't need Android
sudo rm -rf /usr/share/dotnet # will release about 20GB if you don't need .NET
sudo df -h
- if: ${{ steps.cache-check.outputs.cache-hit != 'true' }}
name: Build toolkit
run: |
make build
- if: ${{ steps.cache-check.outputs.cache-hit != 'true' }}
name: Install to disk
run: |
Expand Down
8 changes: 4 additions & 4 deletions pkg/action/build-disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ func (b *BuildDiskAction) BuildDiskRun() (err error) { //nolint:gocyclo
}

// Install grub
err = b.bootloader.InstallConfig(activeRoot, b.roots[constants.StatePartName])
err = b.bootloader.InstallConfig(activeRoot, b.roots[constants.EfiPartName])
if err != nil {
b.cfg.Logger.Errorf("failed installing grub configuration: %s", err.Error())
return err
Expand All @@ -199,7 +199,7 @@ func (b *BuildDiskAction) BuildDiskRun() (err error) { //nolint:gocyclo

grubVars := b.spec.GetGrubLabels()
err = b.bootloader.SetPersistentVariables(
filepath.Join(b.roots[constants.StatePartName], constants.GrubOEMEnv),
filepath.Join(b.roots[constants.EfiPartName], constants.GrubOEMEnv),
grubVars,
)
if err != nil {
Expand All @@ -208,7 +208,7 @@ func (b *BuildDiskAction) BuildDiskRun() (err error) { //nolint:gocyclo
}

err = b.bootloader.InstallEFI(
activeRoot, b.roots[constants.StatePartName],
activeRoot, b.roots[constants.EfiPartName],
b.roots[constants.EfiPartName], b.spec.Partitions.State.FilesystemLabel,
)
if err != nil {
Expand All @@ -217,7 +217,7 @@ func (b *BuildDiskAction) BuildDiskRun() (err error) { //nolint:gocyclo
}

// Rebrand
err = b.bootloader.SetDefaultEntry(b.roots[constants.StatePartName], activeRoot, b.spec.GrubDefEntry)
err = b.bootloader.SetDefaultEntry(b.roots[constants.EfiPartName], activeRoot, b.spec.GrubDefEntry)
if err != nil {
return elementalError.NewFromError(err, elementalError.SetDefaultGrubEntry)
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/action/build-iso.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
)

const (
grubPrefixDir = "/boot/grub2"
grubPrefixDir = "/EFI/BOOT"
isoBootCatalog = "/boot/boot.catalog"
)

Expand Down Expand Up @@ -73,7 +73,7 @@ func NewBuildISOAction(cfg *v1.BuildConfig, spec *v1.LiveISO, opts ...BuildISOAc
}

if b.bootloader == nil {
b.bootloader = bootloader.NewGrub(&cfg.Config, bootloader.WithGrubPrefix(grubPrefixDir))
b.bootloader = bootloader.NewGrub(&cfg.Config)
}

return b
Expand Down
8 changes: 4 additions & 4 deletions pkg/action/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,8 @@ func (i InstallAction) Run() (err error) {
// Install grub
err = i.bootloader.Install(
cnst.WorkingImgDir,
i.spec.Partitions.State.MountPoint,
i.spec.Partitions.State.FilesystemLabel,
i.spec.Partitions.EFI.MountPoint,
i.spec.Partitions.EFI.FilesystemLabel,
)
if err != nil {
return elementalError.NewFromError(err, elementalError.InstallGrub)
Expand All @@ -221,7 +221,7 @@ func (i InstallAction) Run() (err error) {

grubVars := i.spec.GetGrubLabels()
err = i.bootloader.SetPersistentVariables(
filepath.Join(i.spec.Partitions.State.MountPoint, cnst.GrubOEMEnv),
filepath.Join(i.spec.Partitions.EFI.MountPoint, cnst.GrubOEMEnv),
grubVars,
)
if err != nil {
Expand All @@ -231,7 +231,7 @@ func (i InstallAction) Run() (err error) {

// Installation rebrand (only grub for now)
err = i.bootloader.SetDefaultEntry(
i.spec.Partitions.State.MountPoint,
i.spec.Partitions.EFI.MountPoint,
cnst.WorkingImgDir,
i.spec.GrubDefEntry,
)
Expand Down
4 changes: 2 additions & 2 deletions pkg/action/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ var _ = Describe("Install action tests", func() {
{
Name: "device1",
FilesystemLabel: "COS_GRUB",
Type: "ext4",
Type: "vfat",
},
{
Name: "device2",
Expand Down Expand Up @@ -388,7 +388,7 @@ var _ = Describe("Install action tests", func() {
spec.GrubDefEntry = "cOS"
bootloader.ErrorSetDefaultEntry = true
Expect(installer.Run()).NotTo(BeNil())
Expect(runner.MatchMilestones([][]string{{"grub2-editenv", filepath.Join(constants.StateDir, constants.GrubOEMEnv)}}))
Expect(runner.MatchMilestones([][]string{{"grub2-editenv", filepath.Join(constants.EfiDir, constants.GrubOEMEnv)}}))
})
})
})
8 changes: 4 additions & 4 deletions pkg/action/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,8 @@ func (r ResetAction) Run() (err error) {
// install grub
err = r.bootloader.Install(
cnst.WorkingImgDir,
r.spec.Partitions.State.MountPoint,
r.spec.Partitions.State.FilesystemLabel,
r.spec.Partitions.EFI.MountPoint,
r.spec.Partitions.EFI.FilesystemLabel,
)

if err != nil {
Expand Down Expand Up @@ -242,7 +242,7 @@ func (r ResetAction) Run() (err error) {

grubVars := r.spec.GetGrubLabels()
err = r.bootloader.SetPersistentVariables(
filepath.Join(r.spec.Partitions.State.MountPoint, cnst.GrubOEMEnv),
filepath.Join(r.spec.Partitions.EFI.MountPoint, cnst.GrubOEMEnv),
grubVars,
)
if err != nil {
Expand All @@ -252,7 +252,7 @@ func (r ResetAction) Run() (err error) {

// installation rebrand (only grub for now)
err = r.bootloader.SetDefaultEntry(
r.spec.Partitions.State.MountPoint,
r.spec.Partitions.EFI.MountPoint,
cnst.WorkingImgDir,
r.spec.GrubDefEntry,
)
Expand Down
9 changes: 7 additions & 2 deletions pkg/action/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@ func (u *UpgradeAction) Run() (err error) {
return elementalError.NewFromError(err, elementalError.MountRecoveryPartition)
}
cleanup.Push(umount)
umount, err = elemental.MountRWPartition(u.config.Config, u.spec.Partitions.EFI)
if err != nil {
return elementalError.NewFromError(err, elementalError.MountPartitions)
}
cleanup.Push(umount)

// Cleanup transition image file before leaving
cleanup.Push(func() error { return u.remove(upgradeImg.File) })
Expand Down Expand Up @@ -253,7 +258,7 @@ func (u *UpgradeAction) Run() (err error) {

grubVars := u.spec.GetGrubLabels()
err = u.bootloader.SetPersistentVariables(
filepath.Join(u.spec.Partitions.State.MountPoint, constants.GrubOEMEnv),
filepath.Join(u.spec.Partitions.EFI.MountPoint, constants.GrubOEMEnv),
grubVars,
)
if err != nil {
Expand All @@ -265,7 +270,7 @@ func (u *UpgradeAction) Run() (err error) {
if !u.spec.RecoveryUpgrade {
u.Info("rebranding")

err = u.bootloader.SetDefaultEntry(u.spec.Partitions.State.MountPoint, constants.WorkingImgDir, u.spec.GrubDefEntry)
err = u.bootloader.SetDefaultEntry(u.spec.Partitions.EFI.MountPoint, constants.WorkingImgDir, u.spec.GrubDefEntry)
if err != nil {
u.Error("failed setting default entry")
return elementalError.NewFromError(err, elementalError.SetDefaultGrubEntry)
Expand Down
6 changes: 4 additions & 2 deletions pkg/action/upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ var _ = Describe("Runtime Actions", func() {
logger.SetLevel(logrus.DebugLevel)

// Create paths used by tests
utils.MkdirAll(fs, constants.EfiDir, constants.DirPerm)
utils.MkdirAll(fs, fmt.Sprintf("%s/cOS", constants.RunningStateDir), constants.DirPerm)
utils.MkdirAll(fs, fmt.Sprintf("%s/cOS", constants.LiveDir), constants.DirPerm)

Expand All @@ -107,7 +108,8 @@ var _ = Describe("Runtime Actions", func() {
{
Name: "device1",
FilesystemLabel: "COS_GRUB",
Type: "ext4",
Type: "vfat",
MountPoint: constants.EfiDir,
},
{
Name: "device2",
Expand Down Expand Up @@ -337,7 +339,7 @@ var _ = Describe("Runtime Actions", func() {
err := upgrade.Run()
Expect(err).ToNot(HaveOccurred())

actualBytes, err := fs.ReadFile(filepath.Join(constants.RunningStateDir, "grub_oem_env"))
actualBytes, err := fs.ReadFile(filepath.Join(constants.EfiDir, "grub_oem_env"))
Expect(err).To(BeNil())

expected := map[string]string{
Expand Down
2 changes: 1 addition & 1 deletion pkg/bootloader/bootloader_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ import (

func TestTypes(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "booloader type test suite")
RunSpecs(t, "bootloader type test suite")
}
91 changes: 46 additions & 45 deletions pkg/bootloader/grub.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ import (
"github.com/rancher/elemental-toolkit/pkg/utils"

efilib "github.com/canonical/go-efilib"

eleefi "github.com/rancher/elemental-toolkit/pkg/efi"
)

const (
grubPrefix = "/grub2"
grubCfgFile = "grub.cfg"
)

grubEFICfgTmpl = "search --no-floppy --label --set=root %s" +
"\nset prefix=($root)%s" +
"\nconfigfile $prefix/" + grubCfgFile
var (
defaultGrubPrefixes = []string{"/EFI/ELEMENTAL", "/EFI/BOOT"}
)

func getGModulePatterns(module string) []string {
Expand All @@ -57,7 +57,7 @@ type Grub struct {
grubEfiImg string
mokMngr string

grubPrefix string
grubPrefixes []string
configFile string
elementalCfg string
disableBootEntry bool
Expand All @@ -80,8 +80,8 @@ func NewGrub(cfg *v1.Config, opts ...GrubOptions) *Grub {
logger: cfg.Logger,
runner: cfg.Runner,
platform: cfg.Platform,
grubPrefix: grubPrefix,
configFile: grubCfgFile,
grubPrefixes: defaultGrubPrefixes,
elementalCfg: filepath.Join(constants.GrubCfgPath, constants.GrubCfg),
clearBootEntry: true,
secureBoot: secureBoot,
Expand All @@ -107,7 +107,7 @@ func WithSecureBoot(secureboot bool) func(g *Grub) error {

func WithGrubPrefix(prefix string) func(g *Grub) error {
return func(g *Grub) error {
g.grubPrefix = prefix
g.grubPrefixes = append(g.grubPrefixes, prefix)
return nil
}
}
Expand Down Expand Up @@ -174,16 +174,18 @@ func (g *Grub) installModules(rootDir, bootDir string, modules ...string) error
if err != nil {
return err
}
for _, module := range modules {
fileWriteName := filepath.Join(bootDir, g.grubPrefix, fmt.Sprintf("%s-efi", g.platform.Arch), filepath.Base(module))
g.logger.Debugf("Copying %s to %s", module, fileWriteName)
err = utils.MkdirAll(g.fs, filepath.Dir(fileWriteName), constants.DirPerm)
if err != nil {
return fmt.Errorf("error creating destination folder: %v", err)
}
err = utils.CopyFile(g.fs, module, fileWriteName)
if err != nil {
return fmt.Errorf("error copying %s to %s: %s", module, fileWriteName, err.Error())
for _, grubPrefix := range g.grubPrefixes {
for _, module := range modules {
fileWriteName := filepath.Join(bootDir, grubPrefix, fmt.Sprintf("%s-efi", g.platform.Arch), filepath.Base(module))
g.logger.Debugf("Copying %s to %s", module, fileWriteName)
err = utils.MkdirAll(g.fs, filepath.Dir(fileWriteName), constants.DirPerm)
if err != nil {
return fmt.Errorf("error creating destination folder: %v", err)
}
err = utils.CopyFile(g.fs, module, fileWriteName)
if err != nil {
return fmt.Errorf("error copying %s to %s: %s", module, fileWriteName, err.Error())
}
}
}
return nil
Expand All @@ -208,15 +210,15 @@ func (g *Grub) InstallEFI(rootDir, bootDir, efiDir, deviceLabel string) error {
return nil
}

func (g *Grub) InstallEFIFallbackBinaries(rootDir, efiDir, deviceLabel string) error {
return g.installEFIPartitionBinaries(rootDir, efiDir, constants.FallbackEFIPath, deviceLabel)
func (g *Grub) InstallEFIFallbackBinaries(rootDir, efiDir, _ string) error {
return g.installEFIPartitionBinaries(rootDir, efiDir, constants.FallbackEFIPath)
}

func (g *Grub) InstallEFIElementalBinaries(rootDir, efiDir, deviceLabel string) error {
return g.installEFIPartitionBinaries(rootDir, efiDir, constants.EntryEFIPath, deviceLabel)
func (g *Grub) InstallEFIElementalBinaries(rootDir, efiDir, _ string) error {
return g.installEFIPartitionBinaries(rootDir, efiDir, constants.EntryEFIPath)
}

func (g *Grub) installEFIPartitionBinaries(rootDir, efiDir, efiPath, deviceLabel string) error {
func (g *Grub) installEFIPartitionBinaries(rootDir, efiDir, efiPath string) error {
err := g.findEFIImages(rootDir)
if err != nil {
return err
Expand Down Expand Up @@ -274,12 +276,6 @@ func (g *Grub) installEFIPartitionBinaries(rootDir, efiDir, efiPath, deviceLabel
return fmt.Errorf("failed copying %s to %s: %s", g.grubEfiImg, installPath, err.Error())
}

grubCfgContent := []byte(fmt.Sprintf(grubEFICfgTmpl, deviceLabel, g.grubPrefix))
err = g.fs.WriteFile(filepath.Join(efiDir, efiPath, grubCfgFile), grubCfgContent, constants.FilePerm)
if err != nil {
return fmt.Errorf("error writing %s: %s", filepath.Join(efiDir, efiPath, grubCfgFile), err)
}

return nil
}

Expand Down Expand Up @@ -402,8 +398,8 @@ func (g *Grub) SetDefaultEntry(partMountPoint, imgMountPoint, defaultEntry strin
}

// Install installs grub into the device, copy the config file and add any extra TTY to grub
func (g *Grub) Install(rootDir, bootDir, stateLabel string) (err error) {
err = g.InstallEFI(rootDir, bootDir, constants.EfiDir, stateLabel)
func (g *Grub) Install(rootDir, bootDir, deviceLabel string) (err error) {
err = g.InstallEFI(rootDir, bootDir, constants.EfiDir, deviceLabel)
if err != nil {
return err
}
Expand All @@ -422,24 +418,29 @@ func (g *Grub) Install(rootDir, bootDir, stateLabel string) (err error) {
return g.InstallConfig(rootDir, bootDir)
}

// InstallConfig installs grub configuraton files to the expected location. rootDir is the root
// of the OS image, bootDir is the folder grub read the configuration from, usually state partition mountpoint
// InstallConfig installs grub configuraton files to the expected location.
// rootDir is the root of the OS image, bootDir is the folder grub read the
// configuration from, usually EFI partition mountpoint
func (g Grub) InstallConfig(rootDir, bootDir string) error {
grubFile := filepath.Join(rootDir, g.elementalCfg)
dstGrubFile := filepath.Join(bootDir, g.grubPrefix, g.configFile)
for _, path := range []string{constants.FallbackEFIPath, constants.EntryEFIPath} {
grubFile := filepath.Join(rootDir, g.elementalCfg)
dstGrubFile := filepath.Join(bootDir, path, g.configFile)

g.logger.Infof("Using grub config file %s", grubFile)
g.logger.Infof("Using grub config file %s", grubFile)

// Create Needed dir under state partition to store the grub.cfg and any needed modules
err := utils.MkdirAll(g.fs, filepath.Join(bootDir, g.grubPrefix), constants.DirPerm)
if err != nil {
return fmt.Errorf("error creating grub dir: %s", err)
}
// Create Needed dir under state partition to store the grub.cfg and any needed modules
err := utils.MkdirAll(g.fs, filepath.Join(bootDir, path), constants.DirPerm)
if err != nil {
return fmt.Errorf("error creating grub dir: %s", err)
}

g.logger.Infof("Copying grub config file from %s to %s", grubFile, dstGrubFile)
err = utils.CopyFile(g.fs, grubFile, dstGrubFile)
if err != nil {
g.logger.Errorf("Failed copying grub config file: %s", err)
g.logger.Infof("Copying grub config file from %s to %s", grubFile, dstGrubFile)
err = utils.CopyFile(g.fs, grubFile, dstGrubFile)
if err != nil {
g.logger.Errorf("Failed copying grub config file: %s", err)
return err
}
}
return err

return nil
}
Loading

0 comments on commit 56b2723

Please sign in to comment.