Skip to content

Commit ddd6b18

Browse files
committed
refactor: generate GRUB images
Simplify the flow a bit by using live partition info, avoid doing some calculations which are already done in the partition code. Remove some steps I believe we don't need to do. Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
1 parent c7aa266 commit ddd6b18

File tree

2 files changed

+35
-73
lines changed

2 files changed

+35
-73
lines changed

cmd/installer/pkg/install/install.go

Lines changed: 30 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ func (i *Installer) handleMeta(ctx context.Context, mode Mode, previousLabel str
499499
return fmt.Errorf("failed to write back META partition data: %w", err)
500500
}
501501

502-
return gptdev.Sync()
502+
return nil
503503
default:
504504
return fmt.Errorf("unknown image mode: %d", mode)
505505
}
@@ -692,11 +692,11 @@ func (i *Installer) formatPartitions(ctx context.Context, mode Mode, parts []par
692692
}
693693
}
694694

695-
if err := i.handleGrubBlocklist(gptdev, pt, parts); err != nil {
695+
if err := i.handleGrubBlocklist(gptdev, pt); err != nil {
696696
return fmt.Errorf("failed to handle GRUB blocklist: %w", err)
697697
}
698698

699-
return gptdev.Sync()
699+
return nil
700700
default:
701701
return fmt.Errorf("unknown image mode: %d", mode)
702702
}
@@ -761,7 +761,7 @@ func (i *Installer) handlePartitionDataPopulation(ctx context.Context, idx int,
761761
}
762762

763763
//nolint:gocyclo,cyclop
764-
func (i *Installer) handleGrubBlocklist(gptdev gpt.Device, pt *gpt.Table, partitionOptions []partition.Options) error {
764+
func (i *Installer) handleGrubBlocklist(gptdev gpt.Device, pt *gpt.Table) error {
765765
if i.options.Arch != "amd64" {
766766
return nil
767767
}
@@ -770,84 +770,53 @@ func (i *Installer) handleGrubBlocklist(gptdev gpt.Device, pt *gpt.Table, partit
770770
return nil
771771
}
772772

773-
efiPartitionInfo := xslices.Filter(partitionOptions, func(p partition.Options) bool {
774-
return p.Label == constants.EFIPartitionLabel
775-
})
776-
777-
if len(efiPartitionInfo) == 0 {
778-
return fmt.Errorf("failed to find EFI partition for GRUB blocklist handling")
779-
}
780-
781773
sectorSize := gptdev.GetSectorSize()
782774

783-
if err := grub.PatchBlocklistsForDiskImage(sectorSize, efiPartitionInfo[0].Size, i.options.MountPrefix); err != nil {
784-
return fmt.Errorf("failed to patch GRUB blocklists: %w", err)
785-
}
775+
// Find the BIOS GRUB partition where the `core.img` is going to be written to.
776+
var (
777+
biosPartitionIndex int
778+
biosPartition *gpt.Partition
779+
)
786780

787-
// handle the BIOS GRUB partition
788-
biosPartitionInfo := xslices.Filter(partitionOptions, func(p partition.Options) bool {
789-
return p.Label == constants.BIOSGrubPartitionLabel
790-
})
781+
for idx, p := range pt.Partitions() {
782+
if p.Name == constants.BIOSGrubPartitionLabel {
783+
biosPartition = p
784+
biosPartitionIndex = idx
791785

792-
if len(biosPartitionInfo) == 0 {
793-
return fmt.Errorf("failed to find BIOS GRUB partition for GRUB blocklist handling")
794-
}
795-
796-
coreImgData, err := os.ReadFile(filepath.Join(i.options.MountPrefix, "core.img"))
797-
if err != nil {
798-
return fmt.Errorf("failed to read core.img: %w", err)
786+
break
787+
}
799788
}
800789

801-
if len(coreImgData) > int(biosPartitionInfo[0].Size) {
802-
return fmt.Errorf("core.img size (%d bytes) exceeds BIOS partition size (%d bytes)", len(coreImgData), biosPartitionInfo[0].Size)
790+
if biosPartition == nil {
791+
return fmt.Errorf("failed to find BOOT partition for GRUB blocklist handling")
803792
}
804793

805-
partitionImageFile := filepath.Join(i.options.MountPrefix, biosPartitionInfo[0].Label+".img")
806-
807-
if err := utils.CreateRawDisk(i.options.Printf, partitionImageFile, int64(biosPartitionInfo[0].Size), true); err != nil {
808-
return fmt.Errorf("failed to create raw disk for partition %s: %w", biosPartitionInfo[0].Label, err)
794+
// Patch boot.img and core.img with blocklist information.
795+
if err := grub.PatchBlocklistsForDiskImage(sectorSize, biosPartition.FirstLBA, i.options.MountPrefix); err != nil {
796+
return fmt.Errorf("failed to patch GRUB blocklists: %w", err)
809797
}
810798

811-
f, err := os.OpenFile(partitionImageFile, os.O_RDWR, 0)
799+
coreImgData, err := os.ReadFile(filepath.Join(i.options.MountPrefix, "core.img"))
812800
if err != nil {
813-
return fmt.Errorf("failed to open BIOS partition image %s for write: %w", partitionImageFile, err)
814-
}
815-
816-
defer f.Close() //nolint:errcheck
817-
818-
if _, err := f.WriteAt(coreImgData, 0); err != nil {
819-
return fmt.Errorf("failed to embed core.img into BIOS partition image: %w", err)
820-
}
821-
822-
biosPartitionIndex := slices.IndexFunc(partitionOptions, func(p partition.Options) bool {
823-
return p.Label == constants.BIOSGrubPartitionLabel
824-
})
825-
826-
if biosPartitionIndex == -1 {
827-
return fmt.Errorf("failed to find BIOS GRUB partition index for GRUB blocklist handling")
801+
return fmt.Errorf("failed to read core.img: %w", err)
828802
}
829803

830804
w, size, err := pt.PartitionWriter(biosPartitionIndex)
831805
if err != nil {
832-
return fmt.Errorf("failed to get partition writer for partition %s: %w", biosPartitionInfo[0].Label, err)
833-
}
834-
835-
if size != int(biosPartitionInfo[0].Size) {
836-
return fmt.Errorf("partition size mismatch for partition %s: expected %d, got %d", biosPartitionInfo[0].Label, biosPartitionInfo[0].Size, size)
806+
return fmt.Errorf("failed to get partition writer for partition %s: %w", biosPartition.Name, err)
837807
}
838808

839-
// WriteAt will not change the Seek position, but this is just to be safe
840-
if _, err := f.Seek(0, io.SeekStart); err != nil {
841-
return fmt.Errorf("failed to seek to start of BIOS partition image %s: %w", partitionImageFile, err)
809+
if len(coreImgData) > size {
810+
return fmt.Errorf("core.img size (%d bytes) exceeds BIOS partition size (%d bytes)", len(coreImgData), size)
842811
}
843812

844-
writtenSize, err := io.Copy(w, f)
813+
writtenSize, err := w.Write(coreImgData)
845814
if err != nil {
846-
return fmt.Errorf("failed to copy partition data for partition %s: %w", biosPartitionInfo[0].Label, err)
815+
return fmt.Errorf("failed to copy partition data for partition %s: %w", biosPartition.Name, err)
847816
}
848817

849-
if writtenSize != int64(size) {
850-
return fmt.Errorf("partition data size mismatch for partition %s: expected %d, got %d", biosPartitionInfo[0].Label, size, writtenSize)
818+
if writtenSize != len(coreImgData) {
819+
return fmt.Errorf("partition data size mismatch for partition %s: expected %d, got %d", biosPartition.Name, len(coreImgData), writtenSize)
851820
}
852821

853822
i.options.Printf("embedded GRUB core.img into BIOS partition image (%d bytes)", len(coreImgData))
@@ -861,7 +830,7 @@ func (i *Installer) handleGrubBlocklist(gptdev gpt.Device, pt *gpt.Table, partit
861830

862831
mbr := make([]byte, 446)
863832

864-
if _, err := bootImg.ReadAt(mbr, 0); err != nil {
833+
if _, err := io.ReadFull(bootImg, mbr); err != nil {
865834
return fmt.Errorf("failed to read MBR from boot.img: %w", err)
866835
}
867836

internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/blocklist.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,11 @@ import (
1919
// - include/grub/i386/pc/boot.h: GRUB_BOOT_MACHINE_KERNEL_SECTOR == 0x5c
2020
// - util/setup.c: write_rootdev() patches boot.img fields and writes sector in LE64
2121
// - core image embedded blocklist continuation at core.img offset 0x1F4.
22-
func PatchBlocklistsForDiskImage(sectorSize uint, efiPartitionSizeBytes uint64, mountPrefix string) error {
22+
func PatchBlocklistsForDiskImage(sectorSize uint, biosBootStartSector uint64, mountPrefix string) error {
2323
if sectorSize == 0 {
2424
return fmt.Errorf("sector size must be set to patch GRUB blocklists")
2525
}
2626

27-
if efiPartitionSizeBytes == 0 {
28-
return fmt.Errorf("EFI partition size must be set to patch GRUB blocklists")
29-
}
30-
3127
// Talos partition layout (GPT): EFI (gpt1, efiPartitionSizeBytes), BIOS (gpt2, 1MiB), BOOT (gpt3)
3228
// BIOS boot partition starts immediately after the EFI partition.
3329
const (
@@ -36,11 +32,6 @@ func PatchBlocklistsForDiskImage(sectorSize uint, efiPartitionSizeBytes uint64,
3632
coreImgBlocklistOffset = 0x1f4 // embedded blocklist continuation inside core.img
3733
)
3834

39-
firstPartitionStart := uint64(1024*1024) / uint64(sectorSize) // first partition starts at 1MiB in sectors
40-
41-
efiPartitionSectors := efiPartitionSizeBytes / uint64(sectorSize) // EFI partition size in sectors
42-
biosBootStartSector := firstPartitionStart + efiPartitionSectors
43-
4435
bootImgPath := filepath.Join(mountPrefix, "boot.img")
4536
coreImgPath := filepath.Join(mountPrefix, "core.img")
4637

@@ -80,8 +71,10 @@ func PatchBlocklistsForDiskImage(sectorSize uint, efiPartitionSizeBytes uint64,
8071
}
8172

8273
// Patch 3: core.img embedded blocklist continuation (LE64) points to start+1
83-
cont := biosBootStartSector + 1
84-
binary.LittleEndian.PutUint64(coreImg[coreImgBlocklistOffset:], cont)
74+
//
75+
// The boot.img only loads the first sector of core.img, so the embedded blocklist
76+
// continuation must point to the second sector of core.img.
77+
binary.LittleEndian.PutUint64(coreImg[coreImgBlocklistOffset:], biosBootStartSector+1)
8578

8679
if err := os.WriteFile(coreImgPath, coreImg, 0o644); err != nil {
8780
return fmt.Errorf("failed to write patched core.img: %w", err)

0 commit comments

Comments
 (0)