By default Slackware uses eLILO as its bootloader ( on UEFI systems, LILO on legacy systems ). eLILO works, it's relatively easy to configure, and if you just want to boot your machine and get to work it is all you need to boot your Slackware box. If you're like me, always tinkering and experimenting with your machine ( if I don't break my machine at least twice a month I probably haven't had time to get on it), then you may be thinking about installing the GRUB. Slackware includes GRUB in the official installation, it's just not installed as the default bootloader. Installing GRUB as the bootloader is pretty easy if you follow the right steps, but there are times when that isn't the case. In this post I will take you through the process of installing GRUB on your machine, and these instructions should work on any Linux distribution, not just Slackware.
I strongly encourage to create a USB recovery bootdisk now if you haven't already. It will make your life easier, especially if you are like me and can't leave well enough alone. I break my machine regularly, but the recovery USB allows me to go in and fix things so I can break it again. In the end it's your choice, but you've been warned.
First, and this is very important, remove any USB drives from your machine, and if you have external storage media connected you should disconnect those as well. Before we get started, make sure your EFI partiion is mounted on /boot/efi that these three kernel modules are loaded:
# make sure you use the device name for your machine
jkenobi@slackerhacks:~$ sudo mount /dev/nvme0n1p1 /boot/efi
# These modules are needed to add a boot entry in the firmware
jkenobi@slackerhacks:~$ for mod in efivarfs efivars dm-mod; do sudo modprobe $mod; done
Most of the time you can just run 'grub-install' from the command line and grub will install just fine. This method however has been known to install the i386 version of GRUB on 64-bit machines, leaving users with an unbootable system. That's why it is important to have a USB bootdisk on hand in case you have to recover from an unbootable state. To make sure GRUB installs correctly, we'll use options to give GRUB the information it needs to install correctly.
kenobi@slackerhacks:~$ sudo grub-install --target=x86_64-efi --boot-directory=/boot --efi-directory=/boot/efi \
>/dev/nvme0n1 --recheck --debug | tee grub.log
# ...
# generate GRUB configuration file
jkenobi@slackerhacks:~$ sudo grub-mkconfig -o /boot/grub/grub.cfg
The --target option let's grub know that we are running a 64-bit machine with EFI firmware and to create the appropriate image. The next two are self-explanatory, explicitly specifying the location of the boot and efi directories. Next we tell grub what device the it is to be installed on. The --recheck option tells grub to delete the device map if one exists and create a new one, and the --debug option generates a ton of output, but we're mainly concerned with the last couple of lines that tell us if grub was able to install a boot entry in the firmware. You have to have efibootmgr installed in order for this to take place. Piping the output to the 'tee' command prints the output to the console and also to a text file which I named grub.log in the current directory. It could be useful if for some reason GRUB doesn't boot. Without the --debug option grub will only print the line 'no errors occurred' or something like that, though doesn't necessarily mean your machine will boot successfully. Finally we let GRUB generate its own configuration file with the 'grub-mkconfig' command.
Before rebooting, let's take a look at the grub.cfg file. Depending on how many different kernels you have in your boot directory, if you open grub.cfg in a text editor you will see something like this:
#
# DO NOT EDIT THIS FILE
#
# It is automatically generated by grub-mkconfig using templates
# from /etc/grub.d and settings from /etc/default/grub
#
### BEGIN /etc/grub.d/00_header ###
if [ -s $prefix/grubenv ]; then
load_env
fi
if [ "${next_entry}" ] ; then
set default="${next_entry}"
set next_entry=
save_env next_entry
set boot_once=true
else
set default="0"
fi
if [ x"${feature_menuentry_id}" = xy ]; then
menuentry_id_option="--id"
else
menuentry_id_option=""
fi
export menuentry_id_option
if [ "${prev_saved_entry}" ]; then
set saved_entry="${prev_saved_entry}"
save_env saved_entry
set prev_saved_entry=
save_env prev_saved_entry
set boot_once=true
fi
function savedefault {
if [ -z "${boot_once}" ]; then
saved_entry="${chosen}"
save_env saved_entry
fi
}
function load_video {
if [ x$feature_all_video_module = xy ]; then
insmod all_video
else
insmod efi_gop
insmod efi_uga
insmod ieee1275_fb
insmod vbe
insmod vga
insmod video_bochs
insmod video_cirrus
fi
}
if [ x$feature_default_font_path = xy ] ; then
font=dejavusansmono
else
insmod part_gpt
insmod xfs
search --no-floppy --fs-uuid --set=root 9ce75585-8ad8-4067-927f-9e6af096983b
font="/usr/share/grub/dejavusansmono.pf2"
fi
if loadfont $font ; then
set gfxmode=auto
load_video
insmod gfxterm
set locale_dir=$prefix/locale
set lang=en_US
insmod gettext
fi
terminal_output gfxterm
if [ x$feature_timeout_style = xy ] ; then
set timeout_style=menu
set timeout=10
# Fallback normal timeout code in case the timeout_style feature is
# unavailable.
else
set timeout=10
fi
### END /etc/grub.d/00_header ###
### BEGIN /etc/grub.d/10_linux ###
menuentry 'Slackware-15.0 GNU/Linux' --class slackware_15_0 --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-9ce75585-8ad8-4067-927f-9e6af096983b' {
load_video
insmod gzio
insmod part_gpt
insmod xfs
insmod ext2
search --no-floppy --fs-uuid --set=root 56ad471c-6ef7-42a9-bd30-d982ec539a06
echo 'Loading Linux 5.15.19 ...'
linux /vmlinuz-huge-5.15.19 root=UUID=9ce75585-8ad8-4067-927f-9e6af096983b ro
echo 'Loading initial ramdisk ...'
initrd /initrd-5.15.19.gz
}
submenu 'Advanced options for Slackware-15.0 GNU/Linux' $menuentry_id_option 'gnulinux-advanced-9ce75585-8ad8-4067-927f-9e6af096983b' {
menuentry 'Slackware-15.0 GNU/Linux, with Linux 5.15.19' --class slackware_15_0 --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.15.19-advanced-9ce75585-8ad8-4067-927f-9e6af096983b' {
load_video
insmod gzio
insmod xfs
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root 56ad471c-6ef7-42a9-bd30-d982ec539a06
echo 'Loading Linux 5.15.19 ...'
linux /vmlinuz-huge-5.15.19 root=UUID=9ce75585-8ad8-4067-927f-9e6af096983b ro
echo 'Loading initial ramdisk ...'
initrd /initrd-5.15.19.gz
}
menuentry 'Slackware-15.0 GNU/Linux, with Linux 5.15.19 (recovery mode)' --class slackware_15_0 --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.15.19-recovery-9ce75585-8ad8-4067-927f-9e6af096983b' {
load_video
insmod gzio
insmod part_gpt
insmod xfs
insmod ext2
search --no-floppy --fs-uuid --set=root 56ad471c-6ef7-42a9-bd30-d982ec539a06
echo 'Loading Linux 5.15.19 ...'
linux /vmlinuz-huge-5.15.19 root=UUID=9ce75585-8ad8-4067-927f-9e6af096983b ro single
echo 'Loading initial ramdisk ...'
initrd /initrd-5.15.19.gz
}
menuentry 'Slackware-15.0 GNU/Linux, with Linux huge' --class slackware_15_0 --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-huge-advanced-9ce75585-8ad8-4067-927f-9e6af096983b' {
load_video
insmod gzio
insmod part_gpt
insmod xfs
insmod ext2
search --no-floppy --fs-uuid --set=root 56ad471c-6ef7-42a9-bd30-d982ec539a06
echo 'Loading Linux huge ...'
linux /vmlinuz-huge root=UUID=9ce75585-8ad8-4067-927f-9e6af096983b ro
echo 'Loading initial ramdisk ...'
initrd /initrd.gz
}
menuentry 'Slackware-15.0 GNU/Linux, with Linux huge (recovery mode)' --class slackware_15_0 --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-huge-recovery-9ce75585-8ad8-4067-927f-9e6af096983b' {
load_video
insmod gzio
insmod part_gpt
insmod xfs
insmod ext2
search --no-floppy --fs-uuid --set=root 56ad471c-6ef7-42a9-bd30-d982ec539a06
echo 'Loading Linux huge ...'
linux /vmlinuz-huge root=UUID=9ce75585-8ad8-4067-927f-9e6af096983b ro single
echo 'Loading initial ramdisk ...'
initrd /initrd.gz
}
menuentry 'Slackware-15.0 GNU/Linux, with Linux 5.15.19' --class slackware_15_0 --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.15.19-advanced-9ce75585-8ad8-4067-927f-9e6af096983b' {
load_video
insmod gzio
insmod part_gpt
insmod ext2
insmod xfs
search --no-floppy --fs-uuid --set=root 56ad471c-6ef7-42a9-bd30-d982ec539a06
echo 'Loading Linux 5.15.19 ...'
linux /vmlinuz-generic-5.15.19 root=UUID=9ce75585-8ad8-4067-927f-9e6af096983b ro
echo 'Loading initial ramdisk ...'
initrd /initrd-5.15.19.gz
}
menuentry 'Slackware-15.0 GNU/Linux, with Linux 5.15.19 (recovery mode)' --class slackware_15_0 --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-5.15.19-recovery-9ce75585-8ad8-4067-927f-9e6af096983b' {
load_video
insmod gzio
insmod part_gpt
insmod ext2
insmod xfs
search --no-floppy --fs-uuid --set=root 56ad471c-6ef7-42a9-bd30-d982ec539a06
echo 'Loading Linux 5.15.19 ...'
linux /vmlinuz-generic-5.15.19 root=UUID=9ce75585-8ad8-4067-927f-9e6af096983b ro single
echo 'Loading initial ramdisk ...'
initrd /initrd-5.15.19.gz
}
menuentry 'Slackware-15.0 GNU/Linux, with Linux generic' --class slackware_15_0 --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-generic-advanced-9ce75585-8ad8-4067-927f-9e6af096983b' {
load_video
insmod gzio
insmod part_gpt
insmod xfs
insmod ext2
search --no-floppy --fs-uuid --set=root 56ad471c-6ef7-42a9-bd30-d982ec539a06
echo 'Loading Linux generic ...'
linux /vmlinuz-generic root=UUID=9ce75585-8ad8-4067-927f-9e6af096983b ro
echo 'Loading initial ramdisk ...'
initrd /initrd.gz
}
menuentry 'Slackware-15.0 GNU/Linux, with Linux generic (recovery mode)' --class slackware_15_0 --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-generic-recovery-9ce75585-8ad8-4067-927f-9e6af096983b' {
load_video
insmod gzio
insmod part_gpt
insmod xfs
insmod ext2
search --no-floppy --fs-uuid --set=root 56ad471c-6ef7-42a9-bd30-d982ec539a06
echo 'Loading Linux generic ...'
linux /vmlinuz-generic root=UUID=9ce75585-8ad8-4067-927f-9e6af096983b ro single
echo 'Loading initial ramdisk ...'
initrd /initrd.gz
}
}
### END /etc/grub.d/10_linux ###
### BEGIN /etc/grub.d/20_linux_xen ###
### END /etc/grub.d/20_linux_xen ###
### BEGIN /etc/grub.d/30_os-prober ###
### END /etc/grub.d/30_os-prober ###
### BEGIN /etc/grub.d/30_uefi-firmware ###
### END /etc/grub.d/30_uefi-firmware ###
### BEGIN /etc/grub.d/40_custom ###
# 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.
### END /etc/grub.d/40_custom ###
### BEGIN /etc/grub.d/41_custom ###
if [ -f ${config_directory}/custom.cfg ]; then
source ${config_directory}/custom.cfg
elif [ -z "${config_directory}" -a -f $prefix/custom.cfg ]; then
source $prefix/custom.cfg
fi
### END /etc/grub.d/41_custom ###
GRUB will make an entry for not only each kernel version, but any symbolic links to a kernel as well. I don't generally run grub-mkconfig myself, but for the sake of this tutorial I included it here. Being the Slacker that I am, I prefer a simpler, less cluttered config file, so I write my own. I usually have three entries for Slackware and one for Windows. The default entry is usually the most recent kernel that I've compiled for my machine, then I have an entry for the generic kernel that ships with Slackware, and an entry to boot into single user mode using the generic kernel. Let's go through the steps real quick.
## /boot/grub/grub.cfg
#
# GRUB configuration file
#
##
# first we let grub know which is our root partition
set root=(hd0,gpt6)
# next we tell grub where to find its modules, configfile, etc.
set prefix=(hd0,gpt6)/boot/grub
# now we load modules for video, filesystem, etc.
insmod efi_gop
insmod efi_uga
insmod vbe
insmod vga
insmod video_bochs
insmod video_cirrus
insmod part_gpt
insmod ext2
# set terminal to text console
terminal_output console
# set timeout
set timeout_style=menu
set timeout=30
This first part of the config file lets grub know where to find its modules so it can boot the kernel. The 'prefix' environment varialble tells grub w86here to find the directory x86_64-efi, where its modules are stored. We then load the modules we need , set the terminal to text mode, and set the timeout to 30 sec before grub boots the default kernel. Now we can set up our entries that will appear in the GRUBs boot menu.
# ....
menuentry 'Slackware Linux 15.0 w/ 5.16.10 kernel' --id default {
insmod part_gpt
insmod ext2
insmod gzio
set root='hd0,gpt6'
echo 'Loading vmlinuz-5.16.10...'
linux /boot/vmlinuz-5.16.10 root=UUID=9ce75585-8ad8-4067-927f-9e6af096983b ro eDP:1920x1080 quiet
echo 'Loading initrd-5.16.10.gz'
initrd /boot/initrd-5.16.10
}
Let's take a look at this entry. The first line specifies a menuentry with the label being the text in quotes. That's what will show in GRUBs menu. The --id tag isn't necessary, but there are situations where it is used to identify an entry. The three modules are loaded so GRUB know we have a GPT disk, extX filesystem, and the gzio module is so that it can unzip the initrd. The set root='hd0,gpt' specifes the root partition so that grub can find the kernel and initrd that we specify. The path to the kernel and initrd is relative to this 'set root= ' entry. I could have put 'set root=(hd0,gpt6)/boot', and then the entry for the kernel would be 'linux /vimlinuz-5.16.10 ...'
On my machines this first menu entry changes periodically. I usually keep a recent stable kernel version here. For the next two entries I use the default kernel for the latest Slackware release, which at this time is Slackware 15.0, with kernel version 5.15.19. I use the generic kernel and an initrd so that I'll always have a bootable system. The entry is the same as above, the only difference being the label and the kernel version number. I then make an exact copy of that entry, adding 'single' to the end of the kernel command line, which causes the system to boot into single user mode for system maintenance/repair.
The final version of my grub.cfg file is at the end of this tutorial. For those of you who are like me and run a dual boot configuration with Windows, I'm going to go over the correct way to boot Windows from grub. It's not that different from booting Linux, except that we use GRUBs chainloader. The key is to understand the Windows boot process. The Windows boot configuration is located in a file called BCD. This file is located on the EFI partition in the /EFI/Microsoft/Boot directory. It contains the same sort of information as grub.cfg - root partition, Windows root directory, the path to the file that starts to boot process - using a different format and syntax, of course. Here's my Windows entry in grub.cfg:
## /boot/grub/grub.cfg
#
# GRUB configuration file
#
##
# ...
# Windows boot entry
menuentry 'Windows 11' --id windows {
insmod part_msdos
insmod part_gpt
insmod fat
insmod chain
set root='hd0,gpt1'
chainloader /EFI/Microsoft/Boot/bootmgfw.efi
}
## end of /boot/grub/grub.cfg
And that's really all there is to it. The main thing is understanding the 'set root= ' command doesn't refer to the OS root partition. It serves as a reference point for GRUB to locate to files required to boot the OS. I'm not certain the part_msdos module is necessary, but it doesn't hurt anything. We also load the 'fat' module because the EFI partition has to formatted with either FAT16 or FAT32 filesystem. The chain module enables the chainloader we use to call the bootmgfw.efi file that initiates the rest of the Windows boot process.
Putting all the pieces together it's plain to see that this config file is much easier to read and understand that the one generated by grub-mkconfig. Simple is generally better, as simple systems are easier to debug. Here is my complete grub.cfg file:
## /boot/grub/grub.cfg
#
# GRUB configuration file
#
##
# first we let grub know which is our root partition
set root=(hd0,gpt6)
# next we tell grub where to find its modules, configfile, etc.
set prefix=(hd0,gpt6)/boot/grub
# now we load modules for video, filesystem, etc.
insmod efi_gop
insmod efi_uga
insmod vbe
insmod vga
insmod video_bochs
insmod video_cirrus
insmod part_gpt
insmod ext2
# set terminal to text console
terminal_output console
# set timeout
set timeout_style=menu
set timeout=30
## Linux boot entries
menuentry 'Slackware Linux 15.0 w/ 5.16.10 kernel' --id default {
insmod part_gpt
insmod ext2
insmod gzio
set root='hd0,gpt6'
echo 'Loading vmlinuz-5.16.10...'
linux /boot/vmlinuz-5.16.10 root=UUID=9ce75585-8ad8-4067-927f-9e6af096983b ro eDP:1920x1080 quiet
echo 'Loading initrd-5.16.10.gz'
initrd /boot/initrd-5.16.10.gz
}
menuentry 'Slackware Linux 15.0 w/ generic-5.15.19 kernel' --id stable {
insmod part_gpt
insmod ext2
insmod gzio
set root='hd0,gpt6'
echo 'Loading vmlinuz-generic-5.15.19...'
linux /boot/vmlinuz-generic-5.15.19 root=UUID=9ce75585-8ad8-4067-927f-9e6af096983b ro eDP:1920x1080 quiet
echo 'Loading initrd-generic-5.15.19.gz'
initrd /boot/initrd-generic-5.15.19.gz
}
menuentry 'Slackware Linux 15.0 w/ generic-5.15.19 kernel(recovery mode)' --id recovery {
insmod part_gpt
insmod ext2
insmod gzio
set root='hd0,gpt6'
echo 'Loading vmlinuz-5.16.10...'
linux /boot/vmlinuz-generic-5.15.19 root=UUID=9ce75585-8ad8-4067-927f-9e6af096983b ro eDP:1920x1080 quiet single
echo 'Loading initrd-generic-5.15.19.gz'
initrd /boot/initrd-generic-5.15.19.gz
}
# Windows boot entry
menuentry 'Windows 11' --id windows {
insmod part_msdos
insmod part_gpt
insmod fat
insmod chain
set root='hd0,gpt1'
chainloader /EFI/Microsoft/Boot/bootmgfw.efi
}
## end of /boot/grub/grub.cfg
Congratulations! You have successfully installed and configured GRUB on your machine, and hopefully the process doesn't seem so intimidating. One of the reasons I run Slackware is because it expects us to take control of our machines on a more granular level. It used be said that if you run Debian or RedHat ( Fedora these days ), you'll learn that distro, but if you run Slackware, you'll learn Linux. Hopefully you found this informative, and if you have any questions leave them in the comments below and I'll do my best to answer them.