Ga naar inhoud

GRUB and LUKS Screwed My System

Introduction

After helping my 76‑year‑old father with his first Linux installation (a bold move for anyone at that age), I wanted to disable the graphical boot splash on my Linux Mint 22.04 system. I edited the GRUB configuration, adding the nomodeset kernel parameter and removing the quiet splash options.

NOTE: The nomodeset flag tells the kernel not to load video drivers during early boot, which can be useful for troubleshooting display problems. quiet suppresses most boot messages and splash shows the graphical boot screen.

What Went Wrong?

After applying the changes, the system became unbootable. Rebooting only gave me the UEFI boot manager; the Linux kernel never started. I rescued the machine with a live USB created using Ventoy and mounted the encrypted LUKS partition from the command line.

Steps I Took

Boot the live environment and executed the following python script Perplexity and i created/drafted/troubleshooted together:

#!/usr/bin/env python3
import subprocess
import os
import sys
import re
from pathlib import Path

def run_cmd(cmd, check=True, capture=True, cwd=None):
    """Run a shell command and return output or raise error."""
    try:
        result = subprocess.run(cmd, shell=True, check=check, 
                              capture_output=capture, text=True, cwd=cwd)
        return result.stdout.strip() if capture else None
    except subprocess.CalledProcessError as e:
        print(f"ERROR: Command failed: {cmd}\n{e.stderr}")
        return None

def get_disk_layout():
    """Auto-discover disk layout from lsblk -f."""
    output = run_cmd("lsblk -f -o NAME,FSTYPE,UUID,MOUNTPOINTS")
    if not output:
        return None

    layout = {}
    lines = output.split('\n')[1:]  # Skip header
    for line in lines:
        parts = line.split(maxsplit=3)
        if len(parts) >= 3:
            name, fstype, uuid = parts[0], parts[1], parts[2]
            if 'crypto_LUKS' in fstype:
                layout['luks'] = name
            elif 'vfat' in fstype or 'FAT32' in fstype:
                layout['efi'] = name
            elif name.endswith('root'):
                layout['root_lv'] = name

    # Find boot partition (ext4 not mounted)
    nvme_parts = [line for line in lines if 'nvme' in line and 'ext4' in line]
    layout['boot'] = nvme_parts[0].split()[0] if nvme_parts else None

    return layout

def confirm_step(message):
    """Interactive confirmation."""
    while True:
        response = input(f"{message} [y/N]: ").lower().strip()
        if response in ['y', 'yes']:
            return True
        elif response in ['n', 'no']:
            return False
        print("Please answer y or n")

def unlock_luks(luks_dev):
    """Unlock LUKS container."""
    print(f"🔓 Unlocking LUKS: {luks_dev}")
    if confirm_step("Enter your LUKS passphrase now?"):
        result = run_cmd(f"cryptsetup open {luks_dev} cryptlvm")
        if result:
            print("✅ LUKS unlocked!")
            return True
    return False

def main():
    print("🛠️  Linux Mint GRUB EFI Recovery Script")
    print("=" * 50)

    # Step 1: Auto-discover layout
    print("\n📊 Discovering disk layout...")
    layout = get_disk_layout()

    if not layout:
        print("❌ Could not auto-detect layout. Run 'lsblk -f' manually.")
        sys.exit(1)

    print("Detected:")
    print(f"  EFI:     {layout.get('efi', 'Not found')}")
    print(f"  LUKS:    {layout.get('luks', 'Not found')}")
    print(f"  Root LV: {layout.get('root_lv', 'Not found')}")
    print(f"  Boot:    {layout.get('boot', 'Not found')}")

    if not confirm_step("\nDoes this look correct?"):
        print("Please correct manually and rerun.")
        sys.exit(1)

    # Step 2: Unlock LUKS + LVM
    luks_dev = layout['luks']
    if not unlock_luks(luks_dev):
        sys.exit(1)

    if run_cmd("vgchange -ay"):
        print("✅ LVM activated")
    else:
        print("⚠️  LVM activation failed, continuing...")

    # Step 3: Mount filesystems
    print("\n📁 Mounting filesystems...")
    mounts = []

    root_lv = layout['root_lv']
    efi_part = layout['efi']
    boot_part = layout['boot']

    if run_cmd(f"mount /{root_lv} /mnt"):
        mounts.append('/mnt')
        print("✅ Root mounted")

    if boot_part and run_cmd(f"mount /{boot_part} /mnt/boot"):
        mounts.append('/mnt/boot')
        print("✅ /boot mounted")

    if run_cmd(f"mkdir -p /mnt/boot/efi") and run_cmd(f"mount /{efi_part} /mnt/boot/efi"):
        mounts.append('/mnt/boot/efi')
        print("✅ EFI mounted")

    # Step 4: Bind mounts
    print("\n🔗 Binding system directories...")
    for bind in ['/dev', '/dev/pts', '/proc', '/sys', '/run']:
        if run_cmd(f"mount --bind {bind} /mnt{bind}"):
            mounts.append(f'/mnt{bind}')
            print(f"✅ {bind} bound")

    # Step 5: Chroot and fix GRUB
    print("\n🐚 Entering chroot...")
    print("Inside chroot, we'll:")
    print("  1. Check EFI contents")
    print("  2. Show current /etc/default/grub")
    print("  3. Fix GRUB config")
    print("  4. Reinstall GRUB + EFI entry")

    if confirm_step("Continue to chroot and fix GRUB?"):

        # Show EFI contents first
        print("\n📂 Checking EFI contents...")
        efi_list = run_cmd("ls -la /mnt/boot/efi/EFI/", capture=False)
        print(efi_list)

        # Show current GRUB config
        print("\n📄 Current /etc/default/grub:")
        grub_config = run_cmd("cat /mnt/etc/default/grub")
        print(grub_config)

        print("\n🔧 Fixing GRUB config...")
        # Create safe GRUB config
        safe_grub = '''GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="Linux Mint"
GRUB_CMDLINE_LINUX_DEFAULT=""
GRUB_CMDLINE_LINUX="nomodeset"
GRUB_DISABLE_OS_PROBER=false
'''

        with open('/mnt/etc/default/grub', 'w') as f:
            f.write(safe_grub)
        print("✅ Safe GRUB config written")

        # Chroot commands
        chroot_cmds = [
            "mount --make-rslave /mnt",
            "chroot /mnt /bin/bash -c 'update-grub'",
            "chroot /mnt /bin/bash -c 'efibootmgr -v'",
            "chroot /mnt /bin/bash -c 'grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=linuxmint --recheck --no-nvram'",
            "chroot /mnt /bin/bash -c 'efibootmgr --create --disk /dev/nvme0n1 --part 1 --label \"Linux Mint\" --loader \"\\\\EFI\\\\linuxmint\\\\grubx64.efi\"'",
            "chroot /mnt /bin/bash -c 'update-grub'"
        ]

        print("\n🚀 Running GRUB repair...")
        for cmd in chroot_cmds:
            print(f"Running: {cmd}")
            if run_cmd(cmd):
                print("✅ OK")
            else:
                print("⚠️  Failed, continuing...")

        print("\n✅ GRUB repair complete!")

    # Step 6: Cleanup
    print("\n🧹 Unmounting...")
    for mount in reversed(mounts):
        run_cmd(f"umount {mount}", check=False)

    print("🔒 Closing LUKS...")
    run_cmd("cryptsetup close cryptlvm", check=False)
    run_cmd("vgchange -an vgmint", check=False)

    print("\n🎉 Recovery complete!")
    print("Reboot now (remove USB): sudo reboot")
    if confirm_step("Reboot now?"):
        run_cmd("reboot")

if __name__ == "__main__":
    # Check if running as root
    if os.geteuid() != 0:
        print("❌ Run as root: sudo python3 grub_recovery.py")
        sys.exit(1)

    # Safety check
    if confirm_step("This will modify your disk. Continue?"):
        main()
    else:
        print("Aborted safely.")

The system booted normally again.

The After‑effects

Even though the system started, video performance was terrible. Cinnamon on X was falling back to software rendering because the amdgpu kernel module never loaded – the nomodeset flag had been left in the configuration.

Symptoms

  • Low frame rates, high CPU usage.
  • Incorrect monitor detection; scaling options unavailable.
  • Flickering and occasional freezes.

Fixing the Grub Configuration Safely

I wanted a way to toggle nomodeset without permanently breaking the system. With the help of Perplexity and gpt‑me, I wrote a small script that: 1. Backs up the current /etc/default/grub. 2. Adds a new menu entry called “Linux Mint (nomodeset)” that includes the nomodeset flag. 3. Leaves the default entry untouched.

#!/bin/bash                                                                                                                                                                
set -e                                                                                                                                                                     

echo "=== Removing nomodeset from default GRUB ==="                                                                                                                        
sudo sed -i 's/^GRUB_CMDLINE_LINUX="nomodeset"$/GRUB_CMDLINE_LINUX=""/' /etc/default/grub                                                                                  

echo "=== Creating nomodeset fallback entry ==="                                                                                                                           
sudo tee /etc/grub.d/40_custom > /dev/null << 'EOF'                                                                                                                        
#!/bin/sh                                                                                                                                                                  
exec tail -n +3 $0                                                                                                                                                         
menuentry 'Linux Mint 22.2 (nomodeset fallback)' --class linuxmint --class gnu-linux --class gnu --class os {                                                              
    load_video                                                                                                                                                             
    insmod gzio                                                                                                                                                            
    insmod part_gpt                                                                                                                                                        
    insmod ext2                                                                                                                                                            
    search --no-floppy --fs-uuid --set=root a9c4e8d5-aa77-4b2d-9b7e-8c3f1e2d4b6a                                                                                           
    linux /vmlinuz-6.14.0-37-generic root=/dev/mapper/vgmint-root ro nomodeset                                                                                             
    initrd /initrd.img-6.14.0-37-generic                                                                                                                                   
}                                                                                                                                                                          
EOF                                                                                                                                                                        

sudo chmod +x /etc/grub.d/40_custom                                                                                                                                        

echo "=== Updating GRUB ==="                                                                                                                                               
sudo update-grub                                                                                                                                                           

echo "=== Done! Reboot to test. Fallback entry available in GRUB menu. ==="

Running the script inside the chroot (or on the installed system) gave me a safe way to test nomodeset when needed, while keeping the normal graphical boot as the default.

Lessons Learned

  • Always keep a backup of /etc/default/grub before editing.
  • Use a custom menu entry rather than overwriting the default line.
  • When dealing with encrypted installations, remember to unlock the LUKS volume and bind‑mount the necessary pseudo‑filesystems before chrooting.
  • Always have a Ventoy enabled boot-disk ready with a recent LTS version of the Ubuntu Mint ISO ready.
  • Tools like Ventoy, Perplexity, and gpt‑me can dramatically speed up troubleshooting, but verify the generated commands before executing them.

Original article written on 2025‑12‑30.