detached b1298dd

fix(boot): rebuild initramfs after Plymouth theme, mask NM-wait-online, add early KMS module

commit
b1298dd11f15126f188acbfe67735407d6ff67f4
author
brice · · 2026-02-27 18:42 UTC
parent
058b81b
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
5 files · +44 -3
M internal/application/commands/install_bootloader.go +1 -0
@@ -18,4 +18,5 @@ type InstallBootloaderCommand struct {
1818 EFIPartition string // EFI partition device path
1919 TargetDisk string // Target disk device path
2020 KernelParamsExtra string // Additional kernel parameters
21+ GPUVendor string // "amd", "intel", "nvidia", "unknown" — used for early KMS module
2122 }
@@ -18,4 +18,5 @@ type InstallBootloaderCommand struct {
18 EFIPartition string // EFI partition device path18 EFIPartition string // EFI partition device path
19 TargetDisk string // Target disk device path19 TargetDisk string // Target disk device path
20 KernelParamsExtra string // Additional kernel parameters20 KernelParamsExtra string // Additional kernel parameters
21+ GPUVendor string // "amd", "intel", "nvidia", "unknown" — used for early KMS module
21 }22 }
M internal/application/handlers/bootloader_handler.go +31 -2
@@ -63,7 +63,7 @@ func (h *BootloaderHandler) Handle(ctx context.Context, cmd commands.InstallBoot
6363 return result, err
6464 }
6565
66- if err := h.configureMkinitcpio(ctx, cmd.MountPoint, cmd.EncryptionType); err != nil {
66+ if err := h.configureMkinitcpio(ctx, cmd.MountPoint, cmd.EncryptionType, cmd.GPUVendor); err != nil {
6767 result.ErrorDetail = err.Error()
6868 return result, err
6969 }
@@ -91,7 +91,7 @@ func (h *BootloaderHandler) Handle(ctx context.Context, cmd commands.InstallBoot
9191 return result, nil
9292 }
9393
94-func (h *BootloaderHandler) configureMkinitcpio(ctx context.Context, mountPoint string, encType disk.EncryptionType) error {
94+func (h *BootloaderHandler) configureMkinitcpio(ctx context.Context, mountPoint string, encType disk.EncryptionType, gpuVendor string) error {
9595 confPath := filepath.Join(mountPoint, "etc", "mkinitcpio.conf")
9696 content, err := h.fs.ReadFile(confPath)
9797 if err != nil {
@@ -105,6 +105,12 @@ func (h *BootloaderHandler) configureMkinitcpio(ctx context.Context, mountPoint
105105 }
106106
107107 updated := replaceHooksLine(string(content), hooks)
108+
109+ // Set the KMS module for early framebuffer so Plymouth loads cleanly.
110+ // Without this, the kms hook has nothing to load and Plymouth appears late.
111+ kmsModule := kmsModuleForGPU(gpuVendor)
112+ updated = replaceModulesLine(updated, kmsModule)
113+
108114 if err := h.fs.WriteFile(confPath, []byte(updated), 0644); err != nil {
109115 h.logger.Error("Failed to write mkinitcpio.conf", "error", err)
110116 return fmt.Errorf("failed to write mkinitcpio.conf: %w", err)
@@ -118,6 +124,29 @@ func (h *BootloaderHandler) configureMkinitcpio(ctx context.Context, mountPoint
118124 return nil
119125 }
120126
127+// kmsModuleForGPU returns the kernel module name required for early KMS on the given GPU vendor.
128+// Returns an empty string for NVIDIA (uses proprietary driver, no early KMS) or unknown GPUs.
129+func kmsModuleForGPU(vendor string) string {
130+ switch vendor {
131+ case "amd":
132+ return "amdgpu"
133+ case "intel":
134+ return "i915"
135+ default:
136+ return ""
137+ }
138+}
139+
140+// replaceModulesLine replaces the MODULES=(...) line in mkinitcpio.conf.
141+// If module is empty, MODULES=() is left unchanged (or set to empty).
142+func replaceModulesLine(content, module string) string {
143+ re := regexp.MustCompile(`(?m)^MODULES=\(.*\)$`)
144+ if module == "" {
145+ return re.ReplaceAllString(content, "MODULES=()")
146+ }
147+ return re.ReplaceAllString(content, fmt.Sprintf("MODULES=(%s)", module))
148+}
149+
121150 func (h *BootloaderHandler) installLimine(ctx context.Context, mountPoint string) error {
122151 limineDir := filepath.Join(mountPoint, "boot", "EFI", "limine")
123152 if err := h.fs.MkdirAll(limineDir, 0755); err != nil {
@@ -63,7 +63,7 @@ func (h *BootloaderHandler) Handle(ctx context.Context, cmd commands.InstallBoot
63 return result, err63 return result, err
64 }64 }
65 65
66- if err := h.configureMkinitcpio(ctx, cmd.MountPoint, cmd.EncryptionType); err != nil {66+ if err := h.configureMkinitcpio(ctx, cmd.MountPoint, cmd.EncryptionType, cmd.GPUVendor); err != nil {
67 result.ErrorDetail = err.Error()67 result.ErrorDetail = err.Error()
68 return result, err68 return result, err
69 }69 }
@@ -91,7 +91,7 @@ func (h *BootloaderHandler) Handle(ctx context.Context, cmd commands.InstallBoot
91 return result, nil91 return result, nil
92 }92 }
93 93
94-func (h *BootloaderHandler) configureMkinitcpio(ctx context.Context, mountPoint string, encType disk.EncryptionType) error {94+func (h *BootloaderHandler) configureMkinitcpio(ctx context.Context, mountPoint string, encType disk.EncryptionType, gpuVendor string) error {
95 confPath := filepath.Join(mountPoint, "etc", "mkinitcpio.conf")95 confPath := filepath.Join(mountPoint, "etc", "mkinitcpio.conf")
96 content, err := h.fs.ReadFile(confPath)96 content, err := h.fs.ReadFile(confPath)
97 if err != nil {97 if err != nil {
@@ -105,6 +105,12 @@ func (h *BootloaderHandler) configureMkinitcpio(ctx context.Context, mountPoint
105 }105 }
106 106
107 updated := replaceHooksLine(string(content), hooks)107 updated := replaceHooksLine(string(content), hooks)
108+
109+ // Set the KMS module for early framebuffer so Plymouth loads cleanly.
110+ // Without this, the kms hook has nothing to load and Plymouth appears late.
111+ kmsModule := kmsModuleForGPU(gpuVendor)
112+ updated = replaceModulesLine(updated, kmsModule)
113+
108 if err := h.fs.WriteFile(confPath, []byte(updated), 0644); err != nil {114 if err := h.fs.WriteFile(confPath, []byte(updated), 0644); err != nil {
109 h.logger.Error("Failed to write mkinitcpio.conf", "error", err)115 h.logger.Error("Failed to write mkinitcpio.conf", "error", err)
110 return fmt.Errorf("failed to write mkinitcpio.conf: %w", err)116 return fmt.Errorf("failed to write mkinitcpio.conf: %w", err)
@@ -118,6 +124,29 @@ func (h *BootloaderHandler) configureMkinitcpio(ctx context.Context, mountPoint
118 return nil124 return nil
119 }125 }
120 126
127+// kmsModuleForGPU returns the kernel module name required for early KMS on the given GPU vendor.
128+// Returns an empty string for NVIDIA (uses proprietary driver, no early KMS) or unknown GPUs.
129+func kmsModuleForGPU(vendor string) string {
130+ switch vendor {
131+ case "amd":
132+ return "amdgpu"
133+ case "intel":
134+ return "i915"
135+ default:
136+ return ""
137+ }
138+}
139+
140+// replaceModulesLine replaces the MODULES=(...) line in mkinitcpio.conf.
141+// If module is empty, MODULES=() is left unchanged (or set to empty).
142+func replaceModulesLine(content, module string) string {
143+ re := regexp.MustCompile(`(?m)^MODULES=\(.*\)$`)
144+ if module == "" {
145+ return re.ReplaceAllString(content, "MODULES=()")
146+ }
147+ return re.ReplaceAllString(content, fmt.Sprintf("MODULES=(%s)", module))
148+}
149+
121 func (h *BootloaderHandler) installLimine(ctx context.Context, mountPoint string) error {150 func (h *BootloaderHandler) installLimine(ctx context.Context, mountPoint string) error {
122 limineDir := filepath.Join(mountPoint, "boot", "EFI", "limine")151 limineDir := filepath.Join(mountPoint, "boot", "EFI", "limine")
123 if err := h.fs.MkdirAll(limineDir, 0755); err != nil {152 if err := h.fs.MkdirAll(limineDir, 0755); err != nil {
M internal/application/handlers/configure_system_handler.go +5 -0
@@ -180,6 +180,11 @@ func (h *ConfigureSystemHandler) Handle(ctx context.Context, cmd commands.Config
180180 return result, err
181181 }
182182
183+ // Mask NetworkManager-wait-online to prevent boot delays when no network is immediately available.
184+ if err := h.chrExec.ChrootSystemctl(ctx, h.logger.LogPath(), cmd.MountPoint, "mask", "NetworkManager-wait-online.service"); err != nil {
185+ h.logger.Warn("Failed to mask NetworkManager-wait-online.service", "error", err)
186+ }
187+
183188 // Migrate iwd WiFi credentials from live ISO to NetworkManager profiles in the installed system.
184189 if err := h.migrateIWDProfiles(cmd.MountPoint); err != nil {
185190 // Non-fatal: log and continue. User can configure WiFi manually after boot.
@@ -180,6 +180,11 @@ func (h *ConfigureSystemHandler) Handle(ctx context.Context, cmd commands.Config
180 return result, err180 return result, err
181 }181 }
182 182
183+ // Mask NetworkManager-wait-online to prevent boot delays when no network is immediately available.
184+ if err := h.chrExec.ChrootSystemctl(ctx, h.logger.LogPath(), cmd.MountPoint, "mask", "NetworkManager-wait-online.service"); err != nil {
185+ h.logger.Warn("Failed to mask NetworkManager-wait-online.service", "error", err)
186+ }
187+
183 // Migrate iwd WiFi credentials from live ISO to NetworkManager profiles in the installed system.188 // Migrate iwd WiFi credentials from live ISO to NetworkManager profiles in the installed system.
184 if err := h.migrateIWDProfiles(cmd.MountPoint); err != nil {189 if err := h.migrateIWDProfiles(cmd.MountPoint); err != nil {
185 // Non-fatal: log and continue. User can configure WiFi manually after boot.190 // Non-fatal: log and continue. User can configure WiFi manually after boot.
M internal/application/handlers/post_install_handler.go +6 -1
@@ -98,8 +98,13 @@ func (h *PostInstallHandler) Handle(ctx context.Context, cmd commands.PostInstal
9898 return result, fmt.Errorf("failed to set Plymouth theme: %w", err)
9999 }
100100
101+ // Rebuild initramfs so the Plymouth theme takes effect at boot
102+ if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "mkinitcpio", "-P"); err != nil {
103+ return result, fmt.Errorf("failed to rebuild initramfs after Plymouth theme: %w", err)
104+ }
105+
101106 result.TasksRun = append(result.TasksRun, fmt.Sprintf("plymouth-theme-%s", cmd.PlymouthTheme))
102- h.logger.Info("Plymouth theme installed")
107+ h.logger.Info("Plymouth theme installed and initramfs rebuilt")
103108 }
104109
105110 if err := h.installLimineLogo(cmd.MountPoint); err != nil {
@@ -98,8 +98,13 @@ func (h *PostInstallHandler) Handle(ctx context.Context, cmd commands.PostInstal
98 return result, fmt.Errorf("failed to set Plymouth theme: %w", err)98 return result, fmt.Errorf("failed to set Plymouth theme: %w", err)
99 }99 }
100 100
101+ // Rebuild initramfs so the Plymouth theme takes effect at boot
102+ if _, err := h.chrExec.ExecuteInChroot(ctx, cmd.MountPoint, "mkinitcpio", "-P"); err != nil {
103+ return result, fmt.Errorf("failed to rebuild initramfs after Plymouth theme: %w", err)
104+ }
105+
101 result.TasksRun = append(result.TasksRun, fmt.Sprintf("plymouth-theme-%s", cmd.PlymouthTheme))106 result.TasksRun = append(result.TasksRun, fmt.Sprintf("plymouth-theme-%s", cmd.PlymouthTheme))
102- h.logger.Info("Plymouth theme installed")107+ h.logger.Info("Plymouth theme installed and initramfs rebuilt")
103 }108 }
104 109
105 if err := h.installLimineLogo(cmd.MountPoint); err != nil {110 if err := h.installLimineLogo(cmd.MountPoint); err != nil {
M internal/interfaces/tui/handlers/install_handler.go +1 -0
@@ -184,6 +184,7 @@ func CreateInstallationCommand(app AppContext, formData models.FormData) tea.Cmd
184184 EFIPartition: partitionResult.EFIPartition,
185185 TargetDisk: formData.TargetDisk,
186186 KernelParamsExtra: formData.KernelParamsExtra,
187+ GPUVendor: formData.GPUVendor,
187188 }
188189 if _, err := svc.RunBootloaderSetup(ctx, bootCmd); err != nil {
189190 logger.Error("Bootloader setup failed", "error", err)
@@ -184,6 +184,7 @@ func CreateInstallationCommand(app AppContext, formData models.FormData) tea.Cmd
184 EFIPartition: partitionResult.EFIPartition,184 EFIPartition: partitionResult.EFIPartition,
185 TargetDisk: formData.TargetDisk,185 TargetDisk: formData.TargetDisk,
186 KernelParamsExtra: formData.KernelParamsExtra,186 KernelParamsExtra: formData.KernelParamsExtra,
187+ GPUVendor: formData.GPUVendor,
187 }188 }
188 if _, err := svc.RunBootloaderSetup(ctx, bootCmd); err != nil {189 if _, err := svc.RunBootloaderSetup(ctx, bootCmd); err != nil {
189 logger.Error("Bootloader setup failed", "error", err)190 logger.Error("Bootloader setup failed", "error", err)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 0

    ↑↓ moveenter openesc close