How to change the keymap (keyboard layout) used by the GRUB shell in Gentoo Linux

The default keymap in the GRUB shell is US English. Because Linux has not yet been booted, the GRUB keymap is not governed by the keymap for the Linux console specified in /etc/conf.d/keymaps in the case of OpenRC, or in /etc/vconsole.conf in the case of systemd. This can be inconvenient if your keyboard has a different layout and you need to use the GRUB Rescue Shell. Below I explain how I configured my Gentoo Linux installation to be able to use a different keyboard layout in the GRUB shell.

There are, however, certain limitations to the keymap in the GRUB shell. The official GRUB documentation states the following:

17.4 Input terminal

Firmware console on BIOS, IEEE1275 and ARC doesn’t allow you to enter non-ASCII characters. EFI specification allows for such but author is unaware of any actual implementations. Serial input is currently limited for latin1 (unlikely to change). Own keyboard implementations (at_keyboard and usb_keyboard) supports any key but work on one-char-per-keystroke. So no dead keys or advanced input method. Also there is no keymap change hotkey. In practice it makes difficult to enter any text using non-Latin alphabet. Moreover all current input consumers are limited to ASCII.

Note that the GRUB documentation states ‘ASCII’, not ‘Extended ASCII’. ASCII is limited to codes 000 to 127 (see the character table in e.g. http://www.asciitable.com/).

Some Linux distributions have the utility grub-kbdcomp to generate a GRUB keyboard layout file. grub-kbdcomp is simply a shell script that is a wrapper for the Debian ckbcomp utility and grub-mklayout. There is a Gentoo Portage ebuild for ckbcomp:

root # eix ckbcomp
[I] sys-apps/ckbcomp
     Available versions:  (~)1.164
     Homepage:            https://anonscm.debian.org/cgit/d-i/console-setup.git
     Description:         Compile an XKB keymap for loadkeys

However, I noticed that the latest version currently available in Debian is 1.191 (https://salsa.debian.org/installer-team/console-setup.git), so I created an ebuild ckbcomp-1.191.ebuild in a local overlay on one of my laptops running Gentoo Linux Stable Branch, and I installed ckbcomp-1.191.

# Copyright 1999-2019 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

EAPI=6

DESCRIPTION="Compile an XKB keymap for loadkeys"
HOMEPAGE="https://salsa.debian.org/installer-team/console-setup.git"

if [[ ${PV} == 9999 ]]; then
        inherit git-r3
        EGIT_REPO_URI="https://salsa.debian.org/installer-team/console-setup.git"
else
        SRC_URI="https://salsa.debian.org/installer-team/console-setup/-/archive/${PV}/${P}.tar.gz -> ${P}.tar.gz"
        KEYWORDS="~amd64"
        S="${WORKDIR}"
fi

LICENSE="GPL-2"
SLOT="0"

DEPEND=""
RDEPEND="
        dev-lang/perl:*
        sys-apps/kbd
        x11-misc/xkeyboard-config"

src_compile() {
        :
}

src_install() {
        dobin console-setup-${PV}-*/Keyboard/ckbcomp
}

I have tried the above-mentioned ckbcomp command on my PC BIOS Core i7 laptop running Gentoo Stable with OpenRC and GRUB Version 2.02-r3:

root # eix -I grub
[I] sys-boot/grub
     Available versions:  (2) 2.02-r3(2/2.02-r3)^st **9999(2/9999)^st
       {debug device-mapper doc efiemu +fonts libzfs mount multislot nls sdl static test +themes truetype GRUB_PLATFORMS="coreboot efi-32 efi-64 emu ieee1275 loongson multiboot pc qemu qemu-mips uboot xen xen-32"}
     Installed versions:  2.02-r3(2/2.02-r3)^st(02:33:36 23/03/19)(fonts nls sdl themes truetype -debug -device-mapper -doc -efiemu -libzfs -mount -multislot -static -test GRUB_PLATFORMS="pc -coreboot -efi-32 -efi-64 -emu -ieee1275 -loongson -multiboot -qemu -qemu-mips -uboot -xen -xen-32")
     Homepage:            https://www.gnu.org/software/grub/
     Description:         GNU GRUB boot loader

I used the following steps:

1. Installed sys-apps/ckbcomp.

root # emerge ckbcomp
root # eix ckbcomp
[I] sys-apps/ckbcomp
     Available versions:  (~)1.164 (~)1.191[1]
     Installed versions:  1.191[1](22:09:15 20/04/19)
     Homepage:            https://salsa.debian.org/installer-team/console-setup.git
     Description:         Compile an XKB keymap for loadkeys

[1] "local_overlay" /usr/local/portage

2. Created a new sub-directory to store the GRUB keyboard layout files.

root # mkdir /boot/grub/layouts

3. Converted the X11 keymap to the GRUB keymap. The option for ckbcomp must exist in the directory /usr/share/X11/xkb/symbols/ for this to work.

root # ckbcomp gb extd | grub-mklayout -o /boot/grub/layouts/gb.gkb
Unknown keyboard scan identifier Meta_Tab
Unknown keyboard scan identifier Meta_Tab
Unknown keyboard scan code 0x54
Unknown keyboard scan code 0x65
Unknown keyboard scan code 0x7f

I used the following commands to generate a br.gkb (Brazilian Portuguese keymap) file and a us.gkb (US English keymap) as well, as it is possible to switch keyboard layouts from the GRUB command line using the keymap command, as I show further on:

root # ckbcomp br nodeadkeys | grub-mklayout -o /boot/grub/layouts/br.gkb
Unknown keyboard scan identifier Meta_Tab
Unknown keyboard scan identifier Meta_Tab
Unknown keyboard scan identifier KP_Comma
Unknown keyboard scan identifier KP_Comma
Unknown keyboard scan identifier KP_Comma
Unknown keyboard scan identifier KP_Comma
Unknown keyboard scan code 0x54
Unknown keyboard scan code 0x65
Unknown keyboard scan code 0x7f
root # ckbcomp us | grub-mklayout -o /boot/grub/layouts/us.gkb
Unknown keyboard scan identifier Meta_Tab
Unknown keyboard scan identifier Meta_Tab
Unknown keyboard scan code 0x54
Unknown keyboard scan code 0x65
Unknown keyboard scan code 0x7f

The resulting files can be seen in the directory /boot/grub/layouts/:

root # ls -la /boot/grub/layouts
total 11
drwxr-xr-x 2 root root 1024 Apr 21 21:20 .
drwxr-xr-x 7 root root 1024 Nov 26 00:01 ..
-rw-r--r-- 1 root root 2572 Apr 21 21:29 br.gkb
-rw-r--r-- 1 root root 2572 Apr 21 21:29 gb.gkb
-rw-r--r-- 1 root root 2572 Apr 21 21:30 us.gkb

4. Append ‘GRUB_TERMINAL_INPUT=at_keyboard‘ to /etc/default/grub.

root # grep GRUB_TERMINAL_INPUT /etc/default/grub
GRUB_TERMINAL_INPUT="at_keyboard"

5. Add ‘insmod‘ and ‘keymap‘ lines to /etc/grub.d/40_custom as shown below.

root # tail -n 2 /etc/grub.d/40_custom
insmod keylayouts
keymap $prefix/layouts/gb.gkb

6. Check what locales are available for the keymap.

root # locale --all-locales | grep -i gb
en_GB
en_GB.iso88591
en_GB.utf8

7. Add ‘locale=en_GB‘ to GRUB_CMDLINE_LINUX.

root # grep locale /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="locale=en_GB i915.modeset=1 rcutree.rcu_idle_gp_delay=1 acpi_enforce_resources=lax reboot=force raid=noautodetect resume=/dev/sda2"

8. Regenerate the grub.cfg file.

root # grub-mkconfig -o /boot/grub/grub.cfg
root # grep terminal_input /boot/grub/grub.cfg
terminal_input at_keyboard
root # grep gkb /boot/grub/grub.cfg
keymap $prefix/layouts/gb.gkb
root # grep layouts /boot/grub/grub.cfg
insmod keylayouts
keymap $prefix/layouts/gb.gkb

9. If the machine uses UEFI rather than PC BIOS, update the GRUB files in the EFI directory.

root # grub-install --efi-directory=/boot/efi

10. Reboot to check if the gb keymap has been loaded for the GRUB shell.

root # reboot

When I press ‘c‘ when the GRUB menu appears, I now see the following if I press each key on the second-to-last row of keys on the keyboard:

grub> \zxcvbnm,./

That corresponds to a British English keyboard layout. As I mentioned before, due to GRUB’s limitations only standard ASCII chars are possible, so it is not possible to type characters such as é and è, or symbols such as £ and etc. on the GRUB command line, whatever the keymap.

You can tell if the GRUB keylayouts module is loaded by entering the following command on the GRUB command line:

lsmod

Below is what I then see on the screen of a PC BIOS machine running up-to-date Gentoo Linux (Stable Branch) when I press ‘c‘ when the GRUB menu is displayed.


                                                GNU GRUB  version 2.02~beta3

   Minimal BASH-like line editing is supported. For the first word, TAB lists possible command completions. Anywhere else TAB lists possible device or file completions. ESC at any time exits.


grub> lsmod
Name    Ref Count       Dependencies
minicmd 1
gfxterm_background      1              bitmap,video,extcmd,gfxterm,bitmap_scale,video_colors
bitmap_scale    2               bitmap
video_colors    2
png     1               bitmap,bufio
bitmap  7
search  1               search_label,extcmd,search_fs_file,search_fs_uuid
search_label    2
search_fs_file  2
search_fs_uuid  2
at_keyboard     1               boot,keylayouts
keylayouts      3
gfxterm 3               video,font
all_video       1               video_cirrus,video_bochs,vga,vbe
video_cirrus    2               video_fb,pci,video
video_bochs     2               video_fb,pci,video
pci     6
vga     2               video_fb,video
vbe     2               video_fb,video
video_fb        12
font    5               video,bufio
video   24
loadenv 1               extcmd,disk
disk    2
test    1
normal  1               gettext,boot,extcmd,bufio,crypto,terminal
gzio    0
gettext 3
boot    4
extcmd  8
bufio   10
crypto  2
terminal        2
biosdisk        1
part_msdos      2
ext2    4               fshelp
fshelp  5
grub>

Shown below is what I see when I perform the following steps on the GRUB command line:

  1. press each key on the second-to-last line of keys on the keyboard and press Enter;
  2. check which GRUB terminal input module is loaded;
  3. change from the British English keyboard layout to the Brazilian Portuguese keyboard layout;
  4. press each key on the second-to-last line of keys on the keyboard and press Enter;
  5. switch back to the British English keyboard layout;
  6. press each key on the second-to-last line of keys on the keyboard and press Enter;
  7. switch to the US English keyboard layout;
  8. press each key on the second-to-last line of keys on the keyboard and press Enter;
  9. switch back to the British English keyboard layout.

In each case the output on the screen is correct for the keyboard layout selected:

grub> \zxcvbnm,./
error: can't find command `zxcvbnm,./'.
grub> terminal_input
Active input terminals:
at_keyboard
Available input terminals:
console serial_* serial
grub> keymap br
grub> \zxcvbnm,.;
error: can't find command `zxcvbnm,.;'.
grub> keymap gb
grub> \zxcvbnm,./
error: can't find command `zxcvbnm,./'.
grub> keymap us
grub> <zxcvbnm,./
error: syntax error.
error: Incorrect command.
error: syntax error.
grub> keymap gb
grub> 

There is one more caveat…

When the GRUB menu first appears at boot, the following lines are still displayed at the bottom of the GRUB menu:

   Use the ↑ and ↓ keys to select which entry is highlighted.
   Press enter to boot the selected OS, `e' to edit the commands before booting or `c' for a command-line.
The highlighted entry will be executed automatically in 4s.

However, the highlighted entry on the GRUB menu is no longer executed automatically and I have to press ENTER in order to get GRUB to boot Linux. That is not a big deal in my case.

Automatically clearing the /usr/tmp/portage directory in Gentoo Linux

Gentoo Linux has been in use for nine years on one of my old laptops. A couple of days ago I performed the usual rolling update of the installation, but the latest version of a large package that normally takes several hours to compile failed to compile due to a lack of disk space. Sure enough, the command ‘df -h‘ showed me that the root partition was full. After a little digging I discovered that the directory /usr/tmp/portage/ contained a whopping 30GB of directories and files.

Portage uses the directory /usr/tmp/portage/ as a temporary store for the package source code when merging a package. The temporary files are not deleted if a merge fails, but the emerge command should delete them on the next merge of that package. On the other hand the ebuild command does not delete the temporary files, although normally you only use the ebuild command if you are creating a manifest.

Anyway, in the nine years that Gentoo Linux has been installed on the laptop I had never bothered to check that /usr/tmp/portage/ was actually empty, and its contents had slowly increased. The cure to my immediate problem was simply to empty the directory:

root # rm -rf /usr/tmp/portage/*

I doubt the laptop would still be working by the time /usr/tmp/portage would become that full again, but the situation got me thinking: What if I were to create a script to delete the temporary directories and files in /usr/tmp/portage/ at shutdown?

Gentoo Linux on this laptop uses OpenRC so I simply created a file /etc/local.d/99delete_tmp_files_from_failed_merges.stop containing the following code:

#!/bin/bash
# If root partition is more than 90% full, delete any temporary directories and
# files that were left in /var/tmp/portage/ instead of being deleted.
#
# The root partition is on /dev/sda6 and the emerge command must not be running.
#
if [ `pgrep -c emerge` -eq 0 ] && [ `df | awk '/sda6/ {print $5}' | awk -F% '{print $1}'` -gt 90 ]; then
    rm -rf /usr/tmp/portage/*
fi

I made the script executable:

root # chmod +x /etc/local.d/99delete_tmp_files_from_failed_merges.stop

Now, if the root partition is more than 90% full when I shut down the laptop, the script will automatically empty that directory. One less thing to think about.