Author: admin

  • Ultimate Guide: Dynamic GRUB2 Menus – Conditional Booting & OS Detection for Advanced Users

    Introduction: Beyond the Static Boot Menu

    For advanced Linux users, system administrators, and multi-boot enthusiasts, the default GRUB2 boot menu, while functional, often falls short in terms of flexibility and automation. Imagine a bootloader that intelligently detects operating systems, conditionally boots into specific environments based on system state, or even offers a recovery option only when a particular flag is set. This isn’t just a fantasy; it’s entirely achievable with GRUB2’s powerful scripting capabilities.

    This ultimate guide delves deep into the art of dynamic GRUB2 menus, empowering you to move beyond static entries. We’ll explore conditional logic, environment variables, OS detection techniques, and practical scripting examples to create a GRUB2 menu that adapts to your system’s needs, enhancing your multi-boot experience, server management, and disaster recovery strategies.

    GRUB2 Fundamentals: A Quick Recap

    The Core Files: grub.cfg and 40_custom

    At the heart of GRUB2’s configuration lies /boot/grub/grub.cfg. This file is the primary configuration, but it’s generated automatically by update-grub (or grub-mkconfig). Manual edits to grub.cfg are almost always overwritten. Instead, we use a modular approach.

    The recommended way to add custom entries and scripts is by creating executable files in /etc/grub.d/. The most common file for custom entries is /etc/grub.d/40_custom. Files in this directory are sourced by update-grub in numerical and then alphabetical order, and their output forms part of the final grub.cfg. By default, 40_custom contains a placeholder:

    #!/bin/sh
    exec tail -n +3 $0
    # This file provides an easy way to add custom menu entries. Simply type the
    # menu entries you want to add after this comment. Be careful not to change
    # the 'exec tail' line above.

    All your dynamic scripting and custom menu entries will reside here, or in new files you create (e.g., 41_mydynamicboot).

    Environment Variables and Defaults

    GRUB2 relies on variables defined in /etc/default/grub for global settings. Key variables include:

    • GRUB_DEFAULT: Specifies the default boot entry. Can be an integer (0 for first entry), a quoted menu entry title, or saved to boot the last chosen entry.
    • GRUB_TIMEOUT: Sets the delay in seconds before the default entry is booted.
    • GRUB_TIMEOUT_STYLE: Can be hidden, menu, or countdown.

    For dynamic menus, we’ll often manipulate GRUB_DEFAULT and leverage GRUB2’s internal environment variables.

    Unleashing GRUB2 Scripting Power

    GRUB2’s shell-like environment allows for powerful scripting directly within its configuration. This is where the magic of dynamic menus truly begins.

    Conditional Logic: if/else/elif/fi

    The fundamental building block for dynamic behavior is conditional execution. GRUB2 supports familiar if/else/elif/fi constructs:

    if [ condition ]; then
        # commands if condition is true
    elif [ another_condition ]; then
        # commands if another_condition is true
    else
        # commands if no condition is true
    fi

    This syntax is crucial for making decisions about which menu entries to show or which OS to boot.

    Testing Conditions: The ‘test’ Command

    The test command (or [ ] shorthand) is used within if statements to evaluate conditions. Common tests include:

    • test -f /path/to/file: Checks if a file exists.
    • test -d /path/to/directory: Checks if a directory exists.
    • test x = y: Compares strings (x and y). Using x prefix handles empty strings safely.
    • test x != y: Checks for string inequality.

    Example: Check if a recovery flag file exists:

    if test -f /recovery_mode_flag; then
        echo "Recovery mode enabled!"
    fi

    Variables and Persistence: set & save_env

    GRUB2 allows you to set and manage variables:

    • set VARNAME="value": Defines a variable for the current GRUB session.
    • save_env VARNAME: Persistently saves the current value of VARNAME to /boot/grub/grubenv.
    • load_env: Loads all persistently saved variables from grubenv into the current session.

    To use persistent variables effectively, you typically enable GRUB_DEFAULT=saved in /etc/default/grub and then use save_env in your custom entries to remember the last booted OS.

    Advanced OS Detection Techniques

    True dynamic menus require the ability to detect the presence and location of operating systems or specific boot environments.

    Identifying Partitions: The ‘search’ Command

    The search command is indispensable for locating partitions without relying on fixed GRUB device names (like (hd0,msdos1)), which can change. It can search by UUID, filesystem label, or even by file presence.

    • search --set=root --fs-uuid UUID_STRING: Sets root to the partition with the given UUID.
    • search --set=root --label LABEL_STRING: Sets root to the partition with the given label.
    • search --set=root --file /path/to/file --hint hd0,msdos1: Finds a partition containing a specific file. The hint improves search speed.

    Example: Finding a Linux partition by UUID:

    search --set=root --fs-uuid a1b2c3d4-e5f6-7890-1234-567890abcdef
    set prefix=(${root})/boot/grub

    Probing for OS Presence

    Once you’ve identified a potential root partition, you can use test -f to check for OS-specific files.

    • For Linux: Check for /vmlinuz, /boot/vmlinuz, /etc/fstab.
    • For Windows: Check for /EFI/Microsoft/Boot/bootmgfw.efi (UEFI) or /bootmgr (BIOS).

    Combined example: Detect a specific Linux installation:

    # Attempt to find the partition by UUID
    search --no-floppy --set=linux_root --fs-uuid a1b2c3d4-e5f6-7890-1234-567890abcdef
    
    if [ "${linux_root}" != "" ]; then
        # Check if a Linux kernel exists on that partition
        if test -f (${linux_root})/boot/vmlinuz; then
            echo "Linux found on ${linux_root}!"
            # You can now define a menuentry using this root
        else
            echo "Linux partition found, but no kernel."
        fi
    else
        echo "Specific Linux partition not found."
    fi

    Practical Dynamic Menu Scenarios & Implementation

    Let’s put these concepts into practice with real-world scenarios. All these examples will be placed into /etc/grub.d/40_custom (or a similar custom file).

    Scenario 1: Default to Last Booted OS

    This is a common desire. GRUB2 supports it natively using saved environment variables.

    First, edit /etc/default/grub and change:

    GRUB_DEFAULT=0
    

    to

    GRUB_DEFAULT=saved
    GRUB_SAVEDEFAULT=true

    Then, in your 40_custom or any menu entry in other /etc/grub.d/ files, ensure the --id option is used, and save_env boot_once is called. This example defines a custom menu entry for an imaginary OS and ensures GRUB remembers if it was selected:

    menuentry 'My Custom OS (Persistent)' --id='my_custom_os' {
        set root=(hd0,msdos2)
        linux /boot/vmlinuz-custom root=/dev/sda2 quiet splash
        initrd /boot/initrd.img-custom
        save_env boot_once
    }

    After booting this entry, grubenv will record boot_once=my_custom_os, making it the default until another entry is selected.

    Scenario 2: Conditional Boot into a Recovery Environment

    Imagine a system that normally boots into a production OS, but if a specific flag file exists (e.g., /force_recovery on your main partition), it should offer a prominent recovery option or even boot directly into it.

    menuentry 'Production System' --id='production_os' {
        search --set=root --label MyProdOSLabel
        linux /boot/vmlinuz root=LABEL=MyProdOSLabel ro quiet splash
        initrd /boot/initrd.img
    }
    
    # --- Conditional Recovery Mode ---
    
    # Search for the main production OS partition to check for the flag file
    search --no-floppy --set=prod_root --label MyProdOSLabel
    
    if [ "${prod_root}" != "" ]; then
        if test -f (${prod_root})/force_recovery; then
            menuentry '!!! EMERGENCY RECOVERY MODE !!!' --id='recovery_mode' {
                # Assuming recovery kernel/initrd are on the same partition or a dedicated one
                set root=${prod_root}
                linux /boot/vmlinuz-recovery root=LABEL=MyProdOSLabel ro recovery_mode
                initrd /boot/initrd.img-recovery
            }
            # Optionally, make recovery the default if the flag is present
            set default="recovery_mode"
            set timeout="10"
        fi
    fi

    This script first defines the regular

  • From Static to Dynamic: Upgrade Your GRUB2 Menu with Advanced Scripting Techniques

    Introduction: Beyond the Static GRUB2 Menu

    GRUB2 (GRand Unified Bootloader, version 2) is the cornerstone of many Linux systems, serving as the primary interface between your hardware’s firmware and your operating system. While incredibly powerful, most users interact with GRUB2 through its default, largely static menu generated by os-prober and basic configuration files. This approach, while functional, often falls short for advanced users, system administrators, and developers who require a more intelligent, adaptive, and dynamic boot environment. Imagine a boot menu that automatically detects new kernel images, offers conditional recovery options, or provides specific entries only when certain conditions are met. This article will guide you through unlocking GRUB2’s full potential by leveraging advanced scripting techniques.

    Moving beyond static entries allows for unparalleled customization, improved system resilience, and a more streamlined user experience. We’ll dive deep into GRUB2’s modular architecture, demonstrating how to write custom scripts that inject dynamic logic into your boot process, transforming your boot menu from a simple list into a powerful, automated decision-maker.

    Understanding GRUB2’s Architecture: The /etc/grub.d/ Directory

    The Core Configuration: /etc/default/grub

    Before diving into scripting, it’s crucial to understand GRUB2’s fundamental configuration mechanism. The primary configuration file, /etc/default/grub, contains variables that control GRUB2’s general behavior, appearance, and default boot options. These variables are read by the GRUB2 configuration generation script, grub-mkconfig, and influence how your final grub.cfg is built. Common examples include:

    • GRUB_DEFAULT: Sets the default boot entry.
    • GRUB_TIMEOUT: Defines the duration GRUB2 waits before booting the default entry.
    • GRUB_CMDLINE_LINUX_DEFAULT: Appends parameters to the Linux kernel command line for regular boots.
    • GRUB_GFXMODE: Sets the graphical resolution of the boot menu.

    Here’s a typical snippet:

    GRUB_DEFAULT=0
    GRUB_TIMEOUT_STYLE=hidden
    GRUB_TIMEOUT=5
    GRUB_DISTRIBUTOR="$(lsb_release -i -s 2> /dev/null || echo Debian)"
    GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
    GRUB_CMDLINE_LINUX=""
    

    The Script Hub: /etc/grub.d/

    The true power of GRUB2’s modularity lies within the /etc/grub.d/ directory. This directory contains a collection of executable shell scripts, each responsible for generating a specific part of the grub.cfg file. When you run sudo grub-mkconfig -o /boot/grub/grub.cfg, these scripts are executed in alphanumeric order, and their output is concatenated to form the final GRUB2 configuration file. This sequential execution allows you to insert custom logic at precise points in the menu generation process.

    Common scripts you’ll find include:

    • 00_header: Sets up global GRUB2 variables and functions.
    • 10_linux: Detects installed Linux kernels and creates menu entries.
    • 20_gnulinux_theme: Configures the GRUB2 theme.
    • 30_os-prober: Detects other operating systems (Windows, other Linux distributions) and adds them to the menu.
    • 40_custom: An empty template script designed for user customizations.

    Crafting Custom GRUB2 Scripts: Your Gateway to Dynamic Menus

    Script Structure and Permissions

    To create a custom GRUB2 script, you’ll place an executable shell script in the /etc/grub.d/ directory. The name of the script dictates its execution order; using prefixes like 06_, 07_, or 41_ ensures it runs before or after specific default scripts. All scripts must start with a shebang and should be executable.

    #!/bin/sh
    set -e
    
    # Your custom GRUB2 menu entry generation logic goes here
    # Use 'cat <> "$GRUB_TMPDIR/grub.cfg"
    # echo "  echo 'Hello from custom entry!'" >> "$GRUB_TMPDIR/grub.cfg"
    # echo "}" >> "$GRUB_TMPDIR/grub.cfg"
    

    After creating your script, make it executable:

    sudo chmod +x /etc/grub.d/XX_your_custom_script
    

    Example 1: Conditional Boot for a Specific ISO Image

    Let’s create a script that adds a menu entry to boot a specific ISO image, but only if that ISO file exists on your system. This is incredibly useful for recovery, testing, or having a portable live environment without needing a separate USB drive.

    #!/bin/sh
    set -e
    
    # Define the path to your custom ISO and a descriptive label
    CUSTOM_ISO="/home/user/isos/ubuntu-22.04.4-desktop-amd64.iso"
    ISO_LABEL="Ubuntu 22.04 Live USB"
    
    # Check if the ISO file exists
    if [ -f "$CUSTOM_ISO" ]; then
        echo "Adding dynamic menu entry for $ISO_LABEL (if '$CUSTOM_ISO' exists)"
        cat << EOF
    menuentry '$ISO_LABEL' --class ubuntu --class os {
        set isofile="$CUSTOM_ISO"
        loopback loop "	x"'
        linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=$isofile splash ---
        initrd (loop)/casper/initrd
    }
    EOF
    else
        echo "Custom ISO '$CUSTOM_ISO' not found. Skipping entry for $ISO_LABEL."
    fi
    

    Save this script as, for example, /etc/grub.d/41_custom_iso_boot and make it executable. Remember to replace /home/user/isos/ubuntu-22.04.4-desktop-amd64.iso with the actual path to your ISO file.

    Example 2: Dynamic Maintenance Mode Entry with Flag File

    Consider a scenario where you want a

  • Mastering UEFI Secure Boot: Custom Key Enrollment on Android Devices Explained

    Introduction to UEFI Secure Boot on Android Platforms

    The security landscape for modern computing devices, including those running Android, is increasingly reliant on robust boot integrity mechanisms. While consumer Android devices predominantly utilize Android Verified Boot (AVB) to ensure the integrity of the operating system, an increasing number of embedded Android systems, specialized industrial hardware, and custom development boards are leveraging the Unified Extensible Firmware Interface (UEFI) Secure Boot. This technology provides a critical layer of protection by ensuring that only trusted software—from the firmware itself to the operating system—can execute during the boot process.

    This expert-level guide delves into the intricate process of mastering UEFI Secure Boot, specifically focusing on custom key enrollment for Android-based systems. Understanding and implementing custom keys allows developers and system integrators to establish their own chain of trust, securing proprietary bootloaders, kernels, and system images against unauthorized modification or execution. This is crucial for supply chain security, compliance, and protecting sensitive data in specialized Android deployments.

    Understanding UEFI Secure Boot Architecture

    UEFI Secure Boot operates on a chain of trust model, relying on several critical key databases stored within the platform’s non-volatile RAM (NVRAM):

    • The Platform Key (PK)

      The PK is the root of trust for the entire Secure Boot process. It can be thought of as the master key that controls who is allowed to manage the other key databases. There should only be one PK on a system.

    • The Key Exchange Key (KEK)

      Signed by the PK, the KEK database contains public keys that are permitted to update or manage the Signature Database (DB) and the Forbidden Signature Database (DBX).

    • The Signature Database (DB)

      Signed by a KEK, the DB database contains public keys and certificates of trusted entities (e.g., operating system bootloaders, drivers). Only software signed by a private key corresponding to a public key in the DB will be allowed to execute.

    • The Forbidden Signature Database (DBX)

      Also signed by a KEK, the DBX database lists hashes or certificates of known malicious or revoked software. Any boot component matching an entry in DBX will be blocked from executing.

    Why Custom Key Enrollment on Android?

    While Android Verified Boot (AVB) provides strong integrity checks for the Android OS image, UEFI Secure Boot operates at a lower level, securing the very first stages of the boot process before AVB even takes over. For typical consumer Android smartphones, OEMs pre-enroll their keys, locking down the boot process. However, for specialized Android-running hardware:

    • Supply Chain Security: Custom keys ensure that only components signed by the device manufacturer or integrator can boot, preventing unauthorized software injection.

    • Proprietary Bootloaders: If you develop a custom UEFI bootloader for your Android-based embedded system, you must sign it with your own keys to function with Secure Boot enabled.

    • Compliance and Certification: Certain industry standards or certifications may require a fully controlled and auditable boot process, which custom Secure Boot keys facilitate.

    • Enhanced Root of Trust: Moving beyond OEM-provided keys to a fully custom-managed set gives complete control over the device’s boot security.

    It is critical to note that direct access to UEFI settings for custom key enrollment on typical consumer Android devices is generally not possible due to OEM restrictions. This guide primarily targets embedded Android systems, custom hardware platforms, or advanced development boards where the UEFI firmware is accessible and configurable.

    Prerequisites and Tools

    To embark on this journey, you’ll need the following:

    • A Linux development machine (e.g., Ubuntu, Debian) with `openssl` installed.

    • `efitools` or `sbsigntools` package (for manipulating EFI signature lists and signing binaries). Install via `sudo apt install efitools sbsigntools`.

    • Access to the target Android device’s UEFI firmware interface. This might be a UEFI shell, an OEM-provided firmware update tool, or in highly specialized cases, a JTAG/SPI programmer.

    • Basic understanding of public-key cryptography and shell scripting.

    Step-by-Step: Generating Your Secure Boot Keys

    The first crucial step is to generate your cryptographic key pair for PK, KEK, and DB. We’ll use OpenSSL for this.

    1. Generate the Platform Key (PK)

    openssl req -new -x509 -newkey rsa:2048 -subj "/CN=MyPlatformKey/" -keyout PK.key -out PK.crt -days 3650 -nodes

    2. Generate the Key Exchange Key (KEK)

    openssl req -new -x509 -newkey rsa:2048 -subj "/CN=MyKeyExchangeKey/" -keyout KEK.key -out KEK.crt -days 3650 -nodes

    3. Generate the Signature Database Key (DB)

    openssl req -new -x509 -newkey rsa:2048 -subj "/CN=MySignatureKey/" -keyout DB.key -out DB.crt -days 3650 -nodes

    You now have three key pairs (`.key` for private, `.crt` for public certificate). Keep your private keys (`.key` files) extremely secure.

    Enrolling Custom Keys into UEFI Firmware

    This is the most critical and platform-dependent step. The goal is to provide your UEFI firmware with the public certificates of your PK, KEK, and DB. The process involves creating authenticated variable update files (often `.auth` files) and then delivering them to the firmware.

    1. Convert Certificates to EFI Signature Lists and Authenticated Variables

    We need to convert our `.crt` files into the format UEFI expects for NVRAM variables, typically `.esl` (EFI Signature List) and then into an authenticated variable update format (`.auth`).

    PK Enrollment:

    cert-to-efi-sig-list -g $(uuidgen) PK.crt PK.esl
    sign-efi-sig-list -k PK.key -c PK.crt PK PK.esl PK.auth

    KEK Enrollment:

    cert-to-efi-sig-list -g $(uuidgen) KEK.crt KEK.esl
    sign-efi-sig-list -k PK.key -c PK.crt KEK KEK.esl KEK.auth

    Note: KEK is signed by PK.

    DB Enrollment:

    cert-to-efi-sig-list -g $(uuidgen) DB.crt DB.esl
    sign-efi-sig-list -k KEK.key -c KEK.crt db DB.esl DB.auth

    Note: DB is signed by KEK.

    2. Enrolling via a UEFI Shell (Conceptual Method)

    If your embedded Android device exposes a UEFI shell, this is often the most straightforward method. You would typically copy the `.auth` files to a FAT32 USB drive, boot into the UEFI shell, and use the `setvar` command. Before enrolling, you might need to put the platform into Setup Mode.

    Shell> fs0:           # Navigate to your USB drive
    Shell> setvar -e PK < PK.auth
    Shell> setvar -e KEK < KEK.auth
    Shell> setvar -e db < DB.auth
    Shell> ResetSystem  # Reboot for changes to take effect

    If you need to clear existing keys (e.g., OEM keys), you can create empty `.esl` files and sign them, then enroll them. For example, to clear PK:

    sign-efi-sig-list -k PK.key -c PK.crt PK /dev/null PK_empty.auth
    setvar -e PK < PK_empty.auth

    3. Enrolling via OEM Tools or Firmware Update Mechanisms

    For many embedded platforms, a UEFI shell may not be directly exposed. Instead, OEMs provide specific tools or a firmware update mechanism that can parse and apply `.auth` files or a custom firmware image containing the updated NVRAM variables. Consult your device’s documentation for the specific procedure. This often involves putting the device into a

  • Deep Dive into GRUB2 Environment Variables: Powering Dynamic Menus with Real-time Data

    Introduction: Unlocking GRUB2’s Dynamic Potential

    GRUB2, the Grand Unified Bootloader, is a powerful and highly configurable boot manager. While most users interact with its static menu entries, GRUB2 possesses a robust scripting engine, including support for environment variables. These variables are the cornerstone for creating dynamic, intelligent boot menus that can adapt to system states, user input, or even pseudo-real-time data. This article will deep dive into GRUB2 environment variables, demonstrating how to leverage them to build sophisticated and responsive boot experiences.

    Understanding GRUB2 Environment Variables

    GRUB2 environment variables function much like shell variables, storing strings that can be referenced and manipulated within GRUB’s scripting environment. They are crucial for controlling boot behavior, customizing appearance, and enabling conditional logic.

    Variable Scope: Persistent vs. Transient

    • Transient Variables: These variables exist only for the current GRUB session. They are defined during the boot process (e.g., in grub.cfg or interactively at the GRUB prompt) and are lost once the operating system boots or GRUB restarts. Most variables you define directly in /boot/grub/grub.cfg or custom scripts are transient.
    • Persistent Variables: GRUB2 also supports persistent environment variables, which are stored in a dedicated file (typically /boot/grub/grubenv) and persist across reboots. These are invaluable for user-defined defaults or states that need to be remembered.

    Setting and Using Variables

    Variables are set using the set command and referenced with a dollar sign prefix ($ or ${}).

    set my_variable="Hello World" # Set a transient variableecho ${my_variable} # Output: Hello World

    For persistent variables, GRUB2 provides the save_env and load_env commands. However, direct manipulation of grubenv is discouraged; instead, modify it through the GRUB prompt or by utilizing the grub-editenv utility from a running Linux system.

    The Power of Custom Variables for Conditional Booting

    Custom variables allow you to introduce sophisticated logic into your boot process. Let’s imagine a scenario where you want to offer a ‘recovery mode’ option only if a specific condition is met, such as a flag set by a system administrator.

    Example: Conditional Recovery Mode

    First, we need a way to set a persistent flag. From your running Linux system, use grub-editenv:

    sudo grub-editenv - set boot_recovery="true" # Sets a persistent variable

    Now, we can use this in a custom GRUB configuration file. Create a file named /etc/grub.d/41_custom_recovery and make it executable:

    sudo nano /etc/grub.d/41_custom_recovery

    Add the following content:

    #!/bin/shset -e# Load persistent variables from grubenv so 'boot_recovery' is available.grub_editenv - read boot_recoveryif [ "x${boot_recovery}" = "xtrue" ] ; thenecho "menuentry 'System Recovery Mode (Conditional)' --class ubuntu --class gnu-linux --class gnu --class os {"echo "  recordfail"echo "  set gfxpayload=keep"echo "  linux /vmlinuz-recovery root=/dev/sda1 ro single"echo "  initrd /initrd.img-recovery"echo "}"fi# Unset the variable after use, or reset it if it was for a one-time recovery.grub_editenv - unset boot_recovery

    Make the script executable and update GRUB:

    sudo chmod +x /etc/grub.d/41_custom_recoverysudo update-grub

    Upon reboot, if boot_recovery was set to true, you will see the

  • GRUB2 Hooks & External Scripts: Integrating Python/Bash for Supercharged Dynamic Menus

    Introduction: Beyond Static Boot Menus

    GRUB2, the Grand Unified Bootloader, is a powerful and flexible boot manager for Linux and other operating systems. While its default configuration typically provides static entries, its true power lies in its extensibility. Imagine a boot menu that dynamically updates based on system state, new kernels, or even external data sources. This is achievable by leveraging GRUB2 hooks and integrating external scripting languages like Bash and Python.

    This article will guide you through the process of creating dynamic GRUB2 menu entries using custom scripts. We’ll explore how GRUB2’s configuration generation process works, how to write and integrate Bash scripts, and finally, how to harness the advanced logic of Python to create truly supercharged, intelligent boot menus.

    Understanding GRUB2’s Configuration Generation

    GRUB2’s primary configuration file, /boot/grub/grub.cfg, is not meant for direct manual editing. Instead, it’s generated by the grub-mkconfig utility, which aggregates configuration snippets from various scripts located in the /etc/grub.d/ directory. This directory is where the magic happens for custom dynamic entries.

    The /etc/grub.d/ Structure

    The files in /etc/grub.d/ are essentially shell scripts that are executed in alphanumeric order by grub-mkconfig. Each script is responsible for printing a specific part of the grub.cfg file to standard output. Some key scripts include:

    • 00_header: Sets up global GRUB parameters.
    • 10_linux: Detects installed Linux kernels and creates menu entries.
    • 20_linux_xen: Handles Xen hypervisor entries.
    • 30_os-prober: Detects other operating systems (Windows, macOS, other Linux distributions).
    • 40_custom: An empty template for user-defined static entries.
    • 40_custom_proxy: A hook point for more advanced custom entries, often used as a template.

    By creating your own script within this directory, you can inject custom menu entries or even modify existing ones during the GRUB configuration generation process.

    Creating a Basic External Script Hook

    Let’s start by creating a simple Bash script that adds a custom menu entry. We’ll place it in /etc/grub.d/ to ensure it’s picked up by grub-mkconfig.

    Step 1: Create the Script File

    Use a number that ensures your script runs at the desired time. For custom entries, a number between 40 and 49 is usually appropriate. Let’s use 45_my_dynamic_entry.

    sudo nano /etc/grub.d/45_my_dynamic_entry

    Step 2: Add Script Content (Bash Example)

    Inside the file, add the following Bash script:

    #!/bin/shset -e# This script adds a custom GRUB2 menu entry.cat << EOFmenuentry 'My Custom Dynamic Entry (Bash)' {    insmod part_gpt    insmod ext2    set root='hd0,gpt2' # Adjust to your root partition    linux /boot/vmlinuz-$(uname -r) root=/dev/sda2 ro quiet splash    initrd /boot/initrd.img-$(uname -r)}EOF

    Explanation:

    • #!/bin/sh: Shebang line indicating it’s a shell script.
    • set -e: Exits immediately if a command exits with a non-zero status.
    • cat << EOF ... EOF: This is a ‘here document’ that prints the enclosed text directly to standard output, which grub-mkconfig then captures into grub.cfg.
    • menuentry '...' { ... }: The standard GRUB2 syntax for a menu entry.
    • insmod part_gpt, insmod ext2: Load necessary GRUB modules for your partition type.
    • set root='hd0,gpt2': Sets the root device for GRUB. Adjust hd0,gpt2 to your actual root partition (e.g., hd0,msdos1 for MBR).
    • linux /boot/vmlinuz-...: Specifies the kernel image and kernel parameters. $(uname -r) dynamically fetches the current running kernel version.
    • initrd /boot/initrd.img-...: Specifies the initial RAM disk image.

    Step 3: Make the Script Executable

    sudo chmod +x /etc/grub.d/45_my_dynamic_entry

    Step 4: Update GRUB Configuration

    Now, regenerate grub.cfg:

    sudo update-grub

    On some systems, the command might be sudo grub-mkconfig -o /boot/grub/grub.cfg. After running this, reboot your system, and you should see

  • Performance Hacking GRUB2: Optimizing Dynamic Boot Menu Load Times with Lean Scripts

    Introduction: The Hidden Cost of a Dynamic Boot Menu

    GRUB2, the Grand Unified Bootloader, is a powerful and highly flexible boot manager for Linux and other operating systems. Its dynamic nature, driven by an array of scripts in /etc/grub.d/, allows it to automatically detect and configure boot entries for various OS installations. While incredibly convenient, this dynamism comes at a price: slower boot menu generation, especially on systems with multiple operating systems, complex partition layouts, or slower storage. This article delves into optimizing GRUB2’s dynamic boot menu load times by crafting lean, custom scripts, thereby reducing the overhead associated with the default configuration generation process.

    Understanding GRUB2’s Configuration Generation

    The primary GRUB2 configuration file, /boot/grub/grub.cfg, is not meant for direct manual editing. Instead, it’s generated by the update-grub (or grub-mkconfig) command, which executes scripts found in /etc/grub.d/. These scripts, prefixed with numbers (e.g., 00_header, 10_linux, 30_os-prober), are executed in alphanumeric order. Each script contributes fragments to the final grub.cfg. The default scripts often perform extensive system scans, such as:

    • Probing all mounted filesystems for Linux kernels (10_linux).
    • Detecting other operating systems on all disks (30_os-prober).
    • Checking for memtest utilities (20_memtest86+).

    These operations, especially 30_os-prober, can be resource-intensive, leading to significant delays during update-grub execution and, consequently, a bloated and slow-loading boot menu.

    Identifying Bottlenecks and Crafting Leaner Alternatives

    The Problem with 30_os-prober

    The 30_os-prober script is often the biggest culprit for slow GRUB2 updates. It exhaustively scans all available partitions for known operating systems (Windows, macOS, other Linux distributions). If you only ever boot a specific set of OSes or want to manually define them, this script is overkill.

    Minimizing Shell Utility Overhead

    Many default scripts rely heavily on shell utilities like grep, awk, and sed for parsing configuration files or command outputs. While powerful, repeated invocation of these external commands within loops or complex pipelines adds overhead. Custom scripts should aim for simpler logic or pre-calculate static values where possible.

    Strategy for Optimization: Disable Defaults, Enable Custom

    1. Disable Unnecessary Default Scripts: The simplest performance gain comes from disabling scripts you don’t need.
    2. Create Custom, Leaner Scripts: For entries you still need (e.g., a specific Windows boot or a custom Linux kernel entry), write a minimal script that directly adds the GRUB commands without exhaustive probing.
    3. Hardcode Static Values: Use UUIDs or explicit device paths (e.g., (hd0,gpt1)) instead of relying on dynamic detection wherever possible.

    Step-by-Step Optimization Guide

    1. Backup Your Existing GRUB Configuration

    Always start by backing up your critical files:

    sudo cp /etc/default/grub /etc/default/grub.bak sudo cp -r /etc/grub.d/ /etc/grub.d.bak/ sudo cp /boot/grub/grub.cfg /boot/grub/grub.cfg.bak

    2. Disable Unwanted Default Scripts

    Navigate to /etc/grub.d/ and make non-executable any script you don’t need. A common candidate for multi-boot systems is 30_os-prober:

    cd /etc/grub.d/ sudo chmod -x 30_os-prober

    You might also consider disabling 20_memtest86+ if you don’t use it, or even 10_linux if you intend to manage all Linux entries manually (though this is less common and requires careful attention).

    3. Crafting Custom Boot Entries (Example: Windows)

    Instead of 30_os-prober, you can create a custom script, for example, 40_custom_windows, to directly add a Windows boot entry. This avoids the entire OS probing process.

    sudo nano /etc/grub.d/40_custom_windows

    Add the following content (adjust hd0,gpt1 and UUID to your specific Windows partition):

    #!/bin/sh exec tail -n +3 $0 # This line makes the script self-executing for GRUB (leave as is) menuentry 'Windows Boot Manager (on /dev/sda1)' --class windows --class os { insmod part_gpt insmod fat if [ x$feature_platform_search_hint = xy ]; then   search --no-floppy --fs-uuid --set=root --hint-bios=hd0,gpt1 --hint-efi=hd0,gpt1 --hint-baremetal=ahci0,gpt1  YOUR_WINDOWS_PARTITION_UUID fi chainloader /efi/Microsoft/Boot/bootmgfw.efi }

    Replace YOUR_WINDOWS_PARTITION_UUID with the actual UUID of your Windows EFI System Partition. You can find this using sudo blkid.

    Make the script executable:

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

    4. Customizing Linux Entries (Advanced)

    For even finer control over Linux entries, you could disable 10_linux and create your own 40_custom_linux. This is typically done to remove recovery entries, specific kernels, or to simplify the menu structure. A basic custom Linux entry might look like this:

    #!/bin/sh exec tail -n +3 $0 menuentry 'My Custom Arch Linux' --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-YOUR_ROOT_UUID' { insmod part_gpt insmod ext2 set root='hd0,gpt2' # Adjust to your root partition linux /boot/vmlinuz-linux root=UUID=YOUR_ROOT_UUID rw initrd /boot/initramfs-linux.img }

    Remember to replace YOUR_ROOT_UUID and hd0,gpt2 with your actual root partition’s UUID and path. This approach gives you absolute control but requires manual updates for new kernels.

    5. Adjusting GRUB Default Settings

    Edit /etc/default/grub to further fine-tune performance and appearance. Common adjustments include:

    • GRUB_TIMEOUT_STYLE=menu: Ensures the menu is always displayed, preventing delays from hidden menus.
    • GRUB_TIMEOUT=5: Sets the menu timeout in seconds. Reduce it for faster booting if you often select the first entry.
    • GRUB_DISABLE_LINUX_RECOVERY=
  • GRUB2 Scripting Lab: Building a Secure, Interactive Bootloader for Multi-OS Android Devices

    The Challenge of Multi-OS Android Bootloading

    Modern Android devices are powerful, but their bootloaders are typically locked down and optimized for a single operating system: Android. This rigid structure presents a significant hurdle for enthusiasts and developers aiming to create multi-OS environments, such as dual-booting Android with a custom Linux distribution (like PostmarketOS or Debian ARM). While Android’s native boot process involves a chain of loaders (from SoC ROM to primary bootloader like Little Kernel/U-Boot, then to the Android kernel), introducing a secondary OS requires a more flexible, user-configurable boot manager.

    This is where GRUB2 (GRand Unified Bootloader version 2) shines. GRUB2 is a highly customizable and powerful bootloader traditionally used in desktop and server Linux environments. Its robust scripting capabilities, support for various file systems, and ability to load different kernels and initramfs images make it an ideal candidate for orchestrating complex multi-OS setups on ARM-based Android devices, provided the bootloader is unlocked and custom flashing is possible.

    Prerequisites and Setup

    Before diving into GRUB2 scripting, ensure you have the necessary tools and a suitable environment.

    Required Tools and Knowledge

    • Linux Host Machine: A desktop or server running a Linux distribution (e.g., Ubuntu, Fedora) is essential for cross-compiling GRUB2 and preparing files.
    • Android SDK Platform Tools: Ensure you have adb and fastboot installed and configured on your Linux host.
    • Target Android Device: This device must have an unlocked bootloader, and ideally, root access. The ability to flash custom partitions via fastboot or a similar utility is crucial. Be aware that flashing custom bootloaders can brick your device if done incorrectly.
    • Basic Linux and GRUB2 Knowledge: Familiarity with Linux command-line operations, partitioning, and GRUB2’s fundamental concepts will be beneficial.
    • Cross-compilation Toolchain: An ARM or AArch64 GNU toolchain (e.g., gcc-aarch64-linux-gnu) is needed to build GRUB2 for your Android device’s architecture.

    Preparing Your Android Device

    1. Backup Your Device: Always perform a full backup of your device’s boot.img, recovery.img, and other critical partitions before making any modifications.

    <code class=

  • DIY Lab Setup: Creating an Android Secure Boot Environment for Kernel Module Signing Testing

    Introduction: Securing Android with Kernel Module Signing

    In the evolving landscape of mobile security, ensuring the integrity of an Android system’s kernel is paramount. Android’s Verified Boot (AVB) provides a robust chain of trust from the hardware root up to the system partition. However, for highly sensitive environments or specialized devices, an additional layer of security is often desired: kernel module signing. This mechanism ensures that only modules signed by a trusted authority can be loaded into the running kernel, preventing malicious or unauthorized code execution.

    This expert-level tutorial guides you through setting up a DIY lab environment to test kernel module signing within an Android Open Source Project (AOSP) build. You’ll learn how to generate signing keys, configure the Android kernel to enforce module signatures, build a signed module, and deploy it to a test device. This setup is crucial for developers, security researchers, and system integrators looking to harden Android deployments.

    Prerequisites for Your Secure Boot Lab

    Before diving into the setup, ensure you have the following:

    • Hardware: An Android device with an unlocked bootloader (e.g., Google Pixel series for best AOSP compatibility) and a powerful host PC (Linux recommended for AOSP builds).
    • Software:
      • AOSP source code synced to a recent branch (e.g., Android 12 or newer).
      • Android SDK/NDK, `repo`, `git`, `make`, `gcc`, `openssl` installed on your host PC.
      • Sufficient disk space (300GB+) and RAM (16GB+) on your host PC.
    • Knowledge: Familiarity with Linux command line, kernel compilation, and basic Android build system concepts.

    Understanding Android’s Verified Boot and Kernel Module Signing

    Android’s Verified Boot establishes a cryptographic chain of trust, verifying each stage of the boot process before executing it. This includes verifying the bootloader, kernel, and system partitions. While Verified Boot ensures the *initial* kernel image is untampered, kernel module signing extends this trust to dynamically loaded kernel modules. If enabled, the kernel will refuse to load any module that is not cryptographically signed by a trusted key, adding a critical defense against rootkits and unauthorized kernel modifications post-boot.

    The Linux kernel itself supports module signing, controlled by specific configuration options. When integrated into an Android context, this feature can be enforced by the Android kernel running on the device, aligning with the overall secure boot philosophy.

    Step 1: Setting Up Your AOSP Build Environment

    First, set up your AOSP environment if you haven’t already. This involves initializing and syncing the repository, then configuring the build environment for your specific device.

    # Install necessary packages (Ubuntu example) sudo apt-get update && sudo apt-get install git-core gnupg flex bison build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 libncurses5 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc bc rsync libssl-dev # Create AOSP source directory mkdir -p ~/android/aosp cd ~/android/aosp # Initialize repo (replace 'master' with a specific branch like 'android-12.0.0_rXX' if needed) repo init -u https://android.googlesource.com/platform/manifest -b master # Sync the AOSP source code repo sync -j$(nproc --all) # Set up the build environment source build/envsetup.sh # Choose your device (e.g., 'aosp_flame-userdebug' for Pixel 4) lunch aosp_flame-userdebug 

    This process can take several hours depending on your internet connection and hardware.

    Step 2: Generating Kernel Module Signing Keys

    You need a public/private key pair to sign your kernel modules. The private key will be used for signing, and the public key (certificate) will be embedded into the kernel to verify signatures.

    # Create a directory for your keys mkdir -p ~/.android-sig-keys cd ~/.android-sig-keys # Create a minimal openssl.cnf file for batch mode cat < openssl.cnf [ req ] default_bits = 4096 default_md = sha512 distinguished_name = req_distinguished_name x509_extensions = v3_ca prompt = no [ req_distinguished_name ] C = US ST = CA L = Mountain View O = Android Kernel Modules OU = Development CN = Android Kernel Signing Key emailAddress = [email protected] [ v3_ca ] subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer basicConstraints = CA:true EOF # Generate the signing key pair openssl req -new -nodes -utf8 -sha512 -days 3650 -batch -x509 -config openssl.cnf -out signing_key.x509 -keyout signing_key.pem 

    This command generates `signing_key.x509` (your public certificate) and `signing_key.pem` (your private key). Keep `signing_key.pem` secure.

    Step 3: Configuring the Android Kernel for Module Signing

    Now, you need to configure your Android kernel to enable module signing support and trust your newly generated key.

    1. Locate your kernel source: Inside your AOSP directory, the kernel source is typically found under `kernel/common/` or `device/google//kernel-/`. For example, for a Pixel 4, it might be in `device/google/flame/kernel-5.4/`. Navigate to the specific kernel branch you’re building.

    2. Modify the kernel configuration: You’ll need to enable several `CONFIG` options in your kernel’s `.config` file or its defconfig fragment. The AOSP build system typically uses defconfig fragments (e.g., `android-base.config`, `android-recommended.config` within `arch/arm64/configs/` or `common/arch/arm64/configs/`). You might add these to a new fragment or an existing one.

      # Example .config entries to add/modify CONFIG_MODULE_SIG=y CONFIG_MODULE_SIG_ALL=y CONFIG_MODULE_SIG_FORCE=y # Forces all modules to be signed CONFIG_MODULE_SIG_SHA512=y # Use SHA512 for signatures CONFIG_SYSTEM_TRUSTED_KEYS="/path/to/signing_key.x509" # IMPORTANT: This needs to point to the cert file. # During kernel build, the path here is relative to the kernel build root. # You will likely need to copy your signing_key.x509 into the kernel source tree, # e.g., kernel/common/certs/signing_key.x509. Then use that path here. CONFIG_MODULE_SIG_KEY="~/.android-sig-keys/signing_key.pem" # This will be set by the Kbuild system for module signing. 

      Note on `CONFIG_SYSTEM_TRUSTED_KEYS`: The kernel build process needs to embed the public key. The path provided to `CONFIG_SYSTEM_TRUSTED_KEYS` should be accessible during the kernel build. A common practice is to copy `signing_key.x509` into `kernel/common/certs/` and reference it as `certs/signing_key.x509`.

    3. Rebuild the kernel: After modifying the kernel configuration, you need to rebuild the kernel and the boot image. From your AOSP root directory, run:

      # Make sure your lunch configuration is still active make bootimage -j$(nproc --all) 

      This will compile your kernel with module signing enabled and create a new `boot.img`.

    Step 4: Building a Custom Signed Kernel Module

    Now let’s create a simple

  • Automating Android Custom ROM Boot: GRUB2 Scripting for One-Click OS Selection

    Introduction: Streamlining Your Multi-ROM Android Experience

    For enthusiasts who frequently test or maintain multiple Android Custom ROMs on a single device, the process of switching between them often involves repetitive manual steps: rebooting to recovery, flashing a new ROM, wiping data, and then rebooting. While advanced recovery solutions like TWRP offer multi-boot capabilities, integrating Android ROMs into a Linux-based GRUB2 bootloader environment can unlock a level of automation and one-click convenience previously unseen. This guide delves into leveraging GRUB2 scripting to create a dynamic boot menu, allowing you to select and boot your desired Android Custom ROM with the ease of a traditional multi-OS setup.

    This advanced technique is particularly useful for developers, ROM testers, or power users who manage devices with multiple partitions dedicated to different Android builds (e.g., AOSP, LineageOS, Pixel Experience). By the end of this tutorial, you’ll have a GRUB2 configuration that can intelligently detect and present your installed Android ROMs for seamless, automated booting.

    Understanding Android Boot Process and GRUB2 Integration

    Before diving into scripting, it’s crucial to understand how Android boots and where GRUB2 fits in. Traditional Linux distributions boot a kernel and an initial ramdisk (initrd). Android, however, uses a specialized boot.img file, which is essentially a concatenation of the kernel image, a ramdisk image, and a few other header details. This boot.img is typically located on a dedicated boot partition.

    GRUB2, as a powerful bootloader, can initiate the boot process for various operating systems. While it natively understands Linux kernels, directly booting an Android boot.img requires a bit more finesse. We’ll utilize GRUB2’s loopback and chainloader commands, combined with custom scripting, to unpack or point to the necessary components within the boot.img or its partition.

    Prerequisites for GRUB2 Android Boot Automation

    • A Linux Host System: This setup assumes your primary bootloader is GRUB2, likely running on a Linux distribution installed alongside or before your Android partitions.
    • Rooted Android Device/System: You’ll need access to the Android file system, often requiring root to identify partitions and potentially extract boot images.
    • Multiple Android Custom ROMs: These ROMs should be installed on separate partitions or distinct logical volumes on your device’s storage.
    • GRUB2 Installed and Configured: Ensure GRUB2 is already your primary bootloader.
    • Basic GRUB2 Knowledge: Familiarity with /etc/grub.d/ and update-grub is beneficial.

    Identifying Android Partitions and Boot Images

    The first step is to locate where your Android ROMs reside on disk. This involves identifying the dedicated boot and system partitions for each Android installation. We’ll use standard Linux disk utilities for this.

    Step 1: Discovering Disk Layout

    Boot into your Linux host system. Open a terminal and use lsblk or fdisk -l to list all storage devices and their partitions. Pay close attention to partition sizes and types, especially any labeled as ‘Android’ or ‘Linux’ that correspond to your custom ROM installations.

    sudo fdisk -l

    Or for a more human-readable tree view:

    lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINT,LABEL

    Identify the partitions corresponding to your Android installations. For example, you might see /dev/sda5, /dev/sda6, etc., each containing a different Android ROM.

    Step 2: Locating the Boot Image or Android Rootfs

    Once you’ve identified the partitions, you need to find the boot.img for each ROM. Often, Android installations have a dedicated boot partition. If not, the kernel and ramdisk components are typically within the root file system. For simplicity, we’ll assume a dedicated boot partition or the ability to mount the Android system partition to locate the necessary kernel and ramdisk.

    Mount one of your Android system partitions (e.g., /dev/sdaX) to inspect its contents:

    sudo mount /dev/sdaX /mnt/android_rom1ls -l /mnt/android_rom1/boot.img # Check if boot.img is directly present

    If boot.img is not directly visible, you might need to identify the kernel and ramdisk paths, which are often found in /kernel or /boot directories within the system partition, or sometimes directly at the root of the system partition. Modern GRUB2 can often boot a boot.img directly using the loopback command.

    GRUB2 Configuration Fundamentals for Custom Entries

    GRUB2 loads its configuration from /boot/grub/grub.cfg, which is typically generated by scripts in /etc/grub.d/. To add custom entries, we usually create a new script (e.g., 40_custom or our own named script) in /etc/grub.d/.

    Basic GRUB2 Menu Entry Structure

    A typical GRUB2 menu entry for a Linux system looks like this:

    menuentry 'My Linux OS' {set root='(hd0,msdos1)'linux /boot/vmlinuz-x.x.x-generic root=/dev/sda1 roquiet splashinitrd /boot/initrd.img-x.x.x-generic}

    For Android, we need to adapt this, primarily focusing on correctly loading the kernel and ramdisk from the boot.img or its partition.

    Scripting for Dynamic Android Boot with GRUB2

    Now, let’s create a custom GRUB2 script in /etc/grub.d/ that will dynamically add menu entries for our Android ROMs. We’ll name it 40_android_roms.

    Step 1: Create the Custom GRUB2 Script

    sudo nano /etc/grub.d/40_android_roms

    Add the following content. This script iterates through predefined Android partition paths and attempts to boot them. You’ll need to customize the ANDROID_ROMS array with your specific partition paths.

    #!/bin/sh -egrubecho "Adding Android Custom ROMs to GRUB menu..."ANDROID_ROMS=(  "(hd0,gpt5)/boot.img:/Android ROM 1 (LineageOS)"  "(hd0,gpt6)/boot.img:/Android ROM 2 (AOSP)"  "(hd0,gpt7)/boot.img:/Android ROM 3 (PixelExperience)")for ROM_ENTRY in "${ANDROID_ROMS[@]}"; do  ROM_PATH=$(echo "$ROM_ENTRY" | cut -d':' -f1)  ROM_LABEL=$(echo "$ROM_ENTRY" | cut -d':' -f2)  echo "Adding $ROM_LABEL to GRUB..."  cat << EOFmenuentry "$ROM_LABEL" {set root='$ROM_PATH'# Attempt to boot boot.img directly if supported by your GRUB2 version# This might require GRUB2's 'loopback' module to be loaded automatically# or manually via 'insmod loopback' (though usually not needed for boot.img)search --no-floppy --fs-uuid --set=root UUID_OF_ANDROID_PARTITION  # Replace with actual UUIDif [ -f /boot.img ]; then    linux /boot.img    # If boot.img is not directly bootable, you might need to extract kernel/ramdisk    # Example: unpack boot.img manually and point to kernel/ramdisk    # linux /path/to/extracted/kernel # root=/dev/sdaX (or UUID) androidboot.hardware=universal # Add kernel params    # initrd /path/to/extracted/ramdiskelif [ -f /kernel ]; then # Fallback for ROMs without a boot.img at root    linux /kernel root=/dev/sdaX androidboot.hardware=universal  # Adjust root/hardware    initrd /ramdisk # Assuming ramdisk is also at root of partitionelse    echo "Warning: Could not find boot.img or kernel/ramdisk on $ROM_PATH"    # Fallback to chainloading if a specific bootloader is installed on the partition    # chainloader +1fi# Example for GRUB2 with loopback.cfg for boot.img (more modern approach)set root='(hd0,gpt5)' # Adjust to your actual partition# If you have specific kernel parameters, add them heredevicetree /dtb_path/if neededinsmod part_gptinsmod ext2# Optional: for specific Android versions or devices, you might need specific kernel parametersset kparams="androidboot.selinux=permissive androidboot.warm_reset=0"# Try to load boot.img and get kernel/ramdisk from itloopback l (hd0,gpt5)/boot.img # Replace (hd0,gpt5) with correct partition# grub-mkimage can create a core.img with specific modules like 'android' to parse boot.img# If your GRUB supports it, 'androidboot' module might be available# linux (l)/kernel root=/dev/sdaX $kparams # If kernel is inside boot.img chainloader (l)+1 # This might work for some boot.img to pass control to its internal bootloaderfi}EOFdone

    Important Customizations:

    • `ANDROID_ROMS` Array: Crucially, replace the example paths like `(hd0,gpt5)/boot.img` with the actual GRUB2 partition identifiers and paths to your `boot.img` files. To find your GRUB2 partition identifier, use `sudo grub-probe –target=fs_uuid /dev/sdaX` for UUID or refer to `(hdX,gpty)` where `X` is disk number, `Y` is partition number.
    • Kernel Parameters: Android kernels often require specific parameters (e.g., `androidboot.selinux=permissive`, `androidboot.hardware=universal`, `root=/dev/sdaX` or `root=UUID=`). These vary by device and ROM. You might need to examine your ROM’s `init.rc` or existing boot scripts to determine the correct parameters.
    • `loopback` and `chainloader`: The script provides a generic approach. Modern GRUB2 versions can often use `loopback` to treat `boot.img` as a file system and extract components. Some Android systems might respond well to `chainloader (loop)/boot.img` or `chainloader +1` if the partition has its own mini-bootloader. The most robust method might involve manually extracting the kernel and ramdisk from `boot.img` (e.g., using `abootimg` or `unyaffs`) and pointing GRUB2 directly to them.

    For direct `boot.img` booting via `loopback` (if your GRUB2 build supports the ‘android’ module or if the `boot.img` is simple enough):

    menuentry "Android ROM via boot.img" {  set root='(hd0,gpt5)' # Your Android system partition  insmod part_gpt  insmod ext2  set bootimg_path="/boot.img" # Path to boot.img on that partition  # Attempt to load using GRUB2's 'android' module if available  # If not, loopback to extract kernel and ramdisk manually (more complex)  if [ -f $bootimg_path ]; then    loopback loop0 $bootimg_path    linux (loop0)/kernel root=/dev/sda5 # Adjust root partition and add kernel params    initrd (loop0)/ramdisk  else    echo "Boot image not found!"    # Fallback or error handling  fi}

    The `linux (loop0)/kernel` syntax works if GRUB2’s `loopback` functionality can parse the `boot.img` and expose the kernel and ramdisk as files within the loop device. This often requires specific GRUB2 modules. A safer, but more involved, approach is to extract `kernel` and `ramdisk.img` from `boot.img` beforehand and store them directly in the GRUB2 accessible partition.

    Step 2: Make the Script Executable

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

    Step 3: Update GRUB2 Configuration

    sudo update-grub

    This command will execute all scripts in /etc/grub.d/, including your new `40_android_roms` script, and generate the `grub.cfg` file.

    Reboot and Test

    Reboot your system. You should now see new entries in your GRUB2 boot menu corresponding to your Android Custom ROMs. Select one and observe if it boots correctly. You might need to iteratively adjust the kernel parameters or partition paths within your `40_android_roms` script based on your device’s specific requirements.

    Troubleshooting and Best Practices

    • Incorrect Kernel Parameters: If Android starts but crashes or enters a boot loop, the kernel parameters are likely incorrect. Common parameters to check include `root=`, `androidboot.hardware=`, `console=`, `loglevel=`.
    • Partition Identification: Double-check your GRUB2 partition identifiers (e.g., `(hd0,gpt5)`) and UUIDs. Mismatched identifiers are a common error.
    • `boot.img` Parsing: Not all GRUB2 builds or `boot.img` formats are compatible with direct `loopback` extraction of kernel/ramdisk. If you face issues, consider manually extracting the kernel and ramdisk from `boot.img` using tools like `abootimg` or `unyaffs` on your Linux host, and then point GRUB2 directly to these extracted files.
    • Backup: Always back up your `grub.cfg` and `boot.img` files before making significant changes.

    Conclusion

    By scripting GRUB2, you transform the cumbersome process of switching between Android Custom ROMs into a streamlined, one-click operation directly from your bootloader. This advanced customization not only saves time but also provides a deeper understanding of the Android boot process and GRUB2’s powerful capabilities. While requiring careful setup and some debugging, the convenience of a dynamic, GRUB2-managed Android multi-boot environment is an invaluable asset for any serious Android enthusiast or developer.

  • GRUB2 Scripting Masterclass: Crafting Dynamic Boot Menus for Android Dual-Boot

    Introduction to GRUB2 and Dynamic Boot Menus

    GRUB2 (Grand Unified Bootloader, Version 2) is the cornerstone of modern Linux boot processes, offering immense flexibility and power. While its default configuration often suffices, truly advanced users can leverage GRUB2’s scripting capabilities to create dynamic boot menus. This masterclass will delve into crafting such menus, specifically focusing on automatically detecting and configuring Android-x86 installations for a seamless dual-boot experience, eliminating the need for manual updates to grub.cfg.

    Why Dynamic Menus?

    Static boot entries in grub.cfg are brittle. Any change to a partition layout, kernel version, or even the addition/removal of an OS necessitates manual editing and regeneration. Dynamic menus, however, automate this. By writing shell scripts that GRUB2 executes during configuration generation, we can detect available operating systems, their versions, and their locations, and then automatically create appropriate boot entries. This is particularly useful for volatile environments like Android-x86, which might reside on a separate partition or a loopback file system.

    GRUB2’s Architecture: The /etc/grub.d Scripts

    GRUB2’s configuration is primarily managed through scripts located in the /etc/grub.d/ directory. Instead of directly editing /boot/grub/grub.cfg, which is overwritten by update-grub (or grub-mkconfig), we interact with these scripts.

    The Role of update-grub

    The update-grub command is a wrapper for grub-mkconfig -o /boot/grub/grub.cfg. When executed, it sequentially runs all executable scripts in /etc/grub.d/. The output of these scripts is then concatenated into the final grub.cfg file. The order of execution is determined by the numeric prefix of the script filenames (e.g., 05_debian_theme runs before 10_linux).

    Key Script Files

    • 00_header: Sets global GRUB2 variables like timeout and default entry.
    • 10_linux: Detects Linux kernels on the current system and generates entries.
    • 20_linux_xen: For Xen hypervisor entries.
    • 30_os-prober: Uses os-prober to detect other OS installations (Windows, macOS, other Linux distributions). This is often where Android-x86 might be detected, but not always reliably or with optimal parameters.
    • 40_custom: An empty script provided for users to add custom, static entries.
    • 41_custom: A variant of 40_custom for custom entries that appear after those generated by 30_os-prober.

    Fundamentals of GRUB2 Scripting

    The scripts in /etc/grub.d/ are BASH scripts. They can use standard shell commands, but critically, they must output GRUB2 configuration commands. These GRUB2 commands are what get written into grub.cfg.

    GRUB2 Commands vs. Shell Commands

    When writing a script in /etc/grub.d/, you are essentially writing a shell script that prints GRUB2 commands. For instance, to set a variable within the shell script, you use variable="value". To output a GRUB2 command that sets a GRUB2 variable, you would use echo "set grub_variable='value'".

    Variables and Conditionals

    Shell variables (e.g., ANDROID_ROOT_UUID) and conditionals (if [ -d "$ANDROID_DIR" ]) are essential for dynamic detection. GRUB2 itself also supports variables (set root=...) and conditionals, but for the detection logic, we rely on the shell script’s capabilities.

    Detecting Android-x86 Installations

    The core of our dynamic menu is reliably finding the Android-x86 installation. Android-x86 typically resides in a directory named /android-x86 (or similar, like /Android) containing a kernel and an initrd.img. It’s often on a separate partition.

    Locating Partitions with GRUB2’s ls Command

    While the detection happens within our shell script, it’s useful to understand how GRUB2 sees partitions. In the GRUB2 rescue shell, ls can list available disks and partitions (e.g., (hd0,msdos1) or (hd0,gpt1)).

    Identifying Android Root with search.file

    Within our script, we can leverage the search.file GRUB2 command. This command searches for a specific file across all available partitions and sets the root variable to the partition containing it. This is more robust than relying on static UUIDs or device names.

    search.file --set=root_android /android-x86/kernel --hint-bios=hd0,msdos1 --hint-efi=hd0,gpt1 --hint-baremetal=ahci0,msdos1

    The --hint-* parameters are optional but can speed up the search by providing likely locations.

    Crafting the Dynamic Android Boot Script

    Let’s create a script that automatically finds and configures Android-x86. We’ll assume Android-x86 is installed in a directory named /android-x86 on a partition.

    Step 1: Create a New Script File

    Create a new executable file in /etc/grub.d/. We’ll name it 42_android_auto so it runs after default OS probing but before 40_custom.

    sudo nano /etc/grub.d/42_android_auto

    Step 2: Script Content – The Core Logic

    Paste the following script content. This script will try to locate /android-x86/kernel and generate a GRUB2 menu entry if found.

    #!/bin/sh -e
    
    # Source some GRUB2 utility functions
    . /usr/share/grub/grub-helpers.sh
    
    # Define Android installation directory name
    ANDROID_DIR_NAME="android-x86"
    
    # Define the kernel file to search for
    ANDROID_KERNEL_FILE="/${ANDROID_DIR_NAME}/kernel"
    
    # Check if an Android kernel exists on any partition
    if grub_file_is_not_empty "${ANDROID_KERNEL_FILE}"; then
        echo "Found Android-x86 installation."
    
        # Use search.file to find the partition containing the kernel
        # and set the root_android variable to its GRUB device name.
        # Using --no-floppy and --fs-uuid is often more reliable.
        GRUB_ANDROID_ROOT_DEVICE="`grub_get_device_from_file "${ANDROID_KERNEL_FILE}"`"
    
        if [ -n "$GRUB_ANDROID_ROOT_DEVICE" ]; then
            # Output the menuentry for GRUB2
            cat << EOF
    menuentry "Android-x86 (Dynamic)" {
        insmod gzio
        insmod part_msdos
        insmod part_gpt
        insmod ext2
    
        set root='${GRUB_ANDROID_ROOT_DEVICE}'
        search --no-floppy --fs-uuid --set=root_uuid ${GRUB_ANDROID_ROOT_DEVICE_UUID}
        # Assuming the kernel is in /android-x86/kernel relative to the root
        linux /${ANDROID_DIR_NAME}/kernel root=/dev/ram0 androidboot.hardware=android_x86 SRC=/${ANDROID_DIR_NAME} quiet CMDLINE_FORCE_PROBE=sata,usb video=-10:1920x1080
        initrd /${ANDROID_DIR_NAME}/initrd.img
    }
    EOF
        else
            echo "Warning: Could not determine GRUB device for Android-x86. Skipping entry."
        fi
    else
        echo "No Android-x86 installation found."
    fi

    Explanation of the Script

    • #!/bin/sh -e: Standard shebang for a POSIX-compliant shell script.
    • . /usr/share/grub/grub-helpers.sh: Sources GRUB2’s helper functions, providing powerful tools like grub_file_is_not_empty and grub_get_device_from_file to simplify partition detection.
    • ANDROID_DIR_NAME, ANDROID_KERNEL_FILE: Variables for easy customization.
    • if grub_file_is_not_empty "${ANDROID_KERNEL_FILE}"; then: This is the critical detection step. It uses the helper function to check if the specified kernel file exists on any mounted partition.
    • GRUB_ANDROID_ROOT_DEVICE="`grub_get_device_from_file "${ANDROID_KERNEL_FILE}"`": If the kernel is found, this helper function determines the GRUB2-style device identifier (e.g., (hd0,msdos1)) for the partition containing the file.
    • cat << EOF ... EOF: This is a "here document" that outputs the actual GRUB2 menuentry block.
    • insmod gzio ... ext2: Loads necessary GRUB2 modules for file system and compression support. Adjust if your Android partition uses a different filesystem (e.g., ntfs, fat).
    • set root='${GRUB_ANDROID_ROOT_DEVICE}': Sets the root partition for the subsequent linux and initrd commands to the dynamically found device.
    • search --no-floppy --fs-uuid --set=root_uuid ${GRUB_ANDROID_ROOT_DEVICE_UUID}: This line is a powerful GRUB2 command for robust partition identification. The grub_get_device_from_file helper provides the UUID, making this extremely reliable across reboots or disk changes.
    • linux /${ANDROID_DIR_NAME}/kernel ...: The actual kernel path and boot parameters. SRC=/${ANDROID_DIR_NAME} is crucial for Android-x86 to find its system files. Adjust video= as needed for your display.
    • initrd /${ANDROID_DIR_NAME}/initrd.img: Specifies the initial RAM disk.

    Step 3: Make the Script Executable

    For GRUB2 to process your script, it must have execute permissions:

    sudo chmod +x /etc/grub.d/42_android_auto

    Step 4: Update GRUB Configuration

    Finally, regenerate your grub.cfg:

    sudo update-grub

    You should see output indicating that your script was run and potentially found Android.

    Testing and Troubleshooting Your Dynamic Menu

    Rebooting and Verification

    Reboot your system. You should now see "Android-x86 (Dynamic)" in your GRUB2 boot menu. Select it and verify that Android boots correctly.

    Common Issues and Debugging Tips

    • Script Not Executing: Ensure /etc/grub.d/42_android_auto has execute permissions (chmod +x).
    • Android Not Found: Double-check the ANDROID_DIR_NAME and ANDROID_KERNEL_FILE variables in your script. Verify the exact path to your Android-x86 kernel.
    • Wrong Boot Parameters: Android-x86 is sensitive to kernel parameters. If it fails to boot, try booting into a working Linux OS, mount the Android partition, and examine its boot/grub/grub.cfg (if it has one) or documentation for correct SRC and other parameters.
    • GRUB2 Rescue Shell: If your system fails to boot, use the GRUB2 rescue shell (often accessed by pressing ‘c’ during boot) to debug. Use commands like ls, search.file, and cat to verify paths and file existence manually.
    • Syntax Errors: Shell script syntax errors or incorrect GRUB2 commands in your echo statements will lead to errors during update-grub or a broken grub.cfg. Carefully review your script.

    Conclusion

    By mastering GRUB2 scripting, you gain unparalleled control over your boot environment. This masterclass has equipped you with the knowledge to craft dynamic boot menu entries for Android-x86, making your dual-boot setup more robust and maintainable. The principles learned here can be extended to dynamically manage other operating systems, custom kernel builds, or specific system utilities, transforming your GRUB2 menu into an intelligent, self-configuring boot manager.