Bug 1892240

Summary: Adjusting the keyboard using "at_keyboard/keymap" doesn't work and leads to hang of Grub or invalid keys [rhel-7.9.z]
Product: Red Hat Enterprise Linux 7 Reporter: Renaud Métrich <rmetrich>
Component: grub2Assignee: Javier Martinez Canillas <fmartine>
Status: CLOSED ERRATA QA Contact: Release Test Team <release-test-team-automation>
Severity: high Docs Contact:
Priority: high    
Version: 7.9CC: fmartine, jreznik, mhavrila, pjanda, sbarcomb
Target Milestone: rcKeywords: OtherQA, Triaged, ZStream
Target Release: ---   
Hardware: All   
OS: All   
Whiteboard:
Fixed In Version: grub2-2.02-0.87.el7_9.5 Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of:
: 1895369 1897587 (view as bug list) Environment:
Last Closed: 2021-03-16 13:48:42 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 1895369, 1897587    

Description Renaud Métrich 2020-10-28 09:47:20 UTC
Description of problem:

A french customer has laptops with french keyboards. He needs to be able to configure the french layout in Grub2 to request entering a password when the user wants to modify the menu.
This totally makes sense, it's really not possible to enter a password when english layout is used but physical keyboard is in french layout.

Applying the procedure below, it appears that keys are mapped wrongly (e.g. physical key 'a' becomes 'y'), making it impossible to perform editing of Grub2 menu or even go to prompt with 'c'.

The issue seems to only happen on physical systems (Lenovo P71, HP Elitebook 8770W), all is fine on QEMU/KVM.

Procedure to set up french layout (no official KCS yet):

1. Generate the french keyboard for Grub

-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
# mkdir -p /boot/grub2/layouts
# zcat /usr/lib/kbd/keymaps/xkb/fr-azerty.map.gz | grub2-mklayout -o /boot/grub2/layouts/fr.gkb
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

The command will complain about "unknown keyboard scan codes", just ignore.

2. Edit /etc/default/grub to allow input for "at_keyboard"

-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
GRUB_TERMINAL_OUTPUT="console"
GRUB_TERMINAL_INPUT="at_keyboard"
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

Remove "GRUB_TERMINAL" if you have the property.

3. Append "keymap fr" to /etc/grub.d/40_custom

-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
echo "keymap fr" >> /etc/grub.d/40_custom
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

4. Rebuild the Grub menu

-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
# grub2-mkconfig -o /etc/grub2.cfg
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------


Version-Release number of selected component (if applicable):

grub2.02-0.87 (RHEL7) but also grub2.2(RHEL8)


How reproducible:

See above, requires apparently physical hardware


Additional info:

Adding debug in at_keyboard and menu code, I can see discrepancies compared to my QEMU/KVM with host having FR keyboard.

QEMU/KVM:
- pressing 'a' shows keycode 0x14 which translates into 0x61 ('a' ascii code)
- pressing 'e' shows keycode 0x08 which translates into 0x65 ('e' ascii code)

Lenovo P71:
- pressing 'a' shows keycode 0x1c which translates into 0x79 ('y' ascii code)
- pressing 'e' shows keycode 0x0d which translates into 0x6a ('j' ascii code)

Comment 2 Renaud Métrich 2020-10-28 10:25:38 UTC
I guess QEMU/KVM does some translation itself, explaining it works.

In Grub2 code, there is a hardcoded keymap for layout_us:

./grub-core/commands/keylayouts.c:
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
static struct grub_keyboard_layout layout_us = { 
  .keyboard_map = { 
    /* Keyboard errors. Handled by driver.  */
    /* 0x00 */   0,   0,   0,   0,  

    /* 0x04 */ 'a',  'b',  'c',  'd', 
    /* 0x08 */ 'e',  'f',  'g',  'h',  'i', 'j', 'k', 'l',
    /* 0x10 */ 'm',  'n',  'o',  'p',  'q', 'r', 's', 't',
    /* 0x18 */ 'u',  'v',  'w',  'x',  'y', 'z', '1', '2',
    /* 0x20 */ '3',  '4',  '5',  '6',  '7', '8', '9', '0',
    /* 0x28 */ '\n', '\e', '\b', '\t', ' ', '-', '=', '[',
    /* According to usage table 0x31 should be mapped to '/'
       but testing with real keyboard shows that 0x32 is remapped to '/'.
       Map 0x31 to 0. 
    */
    /* 0x30 */ ']',   0,   '\\', ';', '\'', '`', ',', '.',
[...]
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------


I think this explains what the customer sees:
Lenovo P71:
- pressing 'a' shows keycode 0x1c which translates into 0x79 ('y' ascii code)
- pressing 'e' shows keycode 0x0d which translates into 0x6a ('j' ascii code)

keycode 0x1c (pressing 'a') maps to "y" indeed from above
keycode 0x0d (pressing 'e') maps to "j" indeed from above

Comment 3 Renaud Métrich 2020-10-29 13:57:09 UTC
Today I enabled "atkeyb" debugging and added some more traces.
I also hacked the #if condition on line 357 below:

-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------
345 static void
346 set_scancodes (void)
347 {
 :
357 #if 0 && !USE_SCANCODE_SET
358   current_set = 1;
359   return;
360 #else
361 
362   grub_keyboard_controller_write (grub_keyboard_controller_orig
363                                   & ~KEYBOARD_AT_TRANSLATE);
364 
365   write_mode (2);
366   current_set = query_mode ();
367   grub_dprintf ("atkeyb", "returned set %d\n", current_set);
368   if (current_set == 2)
369     return;
370 
371   write_mode (1);
372   current_set = query_mode ();
373   grub_dprintf ("atkeyb", "returned set %d\n", current_set);
374   if (current_set == 1)
375     return;
376   grub_printf ("No supported scancode set found\n");
377 #endif
378 }
-------- 8< ---------------- 8< ---------------- 8< ---------------- 8< --------

This has the effect to force entering the #else clause which enables to detect the current_set.
With this current_set was set to "2" and keyboard was working fine after applying the FR keymap.

It's unclear to me how all this works, but for sure with current code (#if !USE_SCANCODE_SET clause on line 357), current_set is hardcoded to 1 which makes the keyboard return invalid keycodes.

Comment 4 Renaud Métrich 2020-10-30 15:08:44 UTC
I could verify that Upstream Grub2 has the exact same issue.

I've submitted a PR and requested phcoder to have a look and explain the current code.

https://github.com/rhboot/grub2/pull/74

Comment 11 Renaud Métrich 2020-12-03 15:28:09 UTC
So I spent almost all my day working on this.

First the patch proposed upstream doesn't work:

- it makes my T460s handle the french keyboard
- but it breaks on my Asus N53SN

For some reason, on my Asus N53SN query_mode() returns "set2" but in fact "set1" is active internally.

Additionally, if I enable "at_keyboard" in UEFI Grub, my QEMU/KVM is UEFI also breaks with the patch.


The good news is executing set_scancode() all the time (so hacking the "#if !USE_SCANCODE" to disable the block) appears to work on all the systems I have:

- my T460s
- my Asus N53SN
- a QEMU/KVM in UEFI

I'm pretty convinced this is the solution to adopt here.

Comment 13 Marek Havrila 2020-12-04 10:37:11 UTC
The patch proposed upstream doesn't work - see Comment 11. Moving this bug back to assigned.

Comment 29 Petr Janda 2021-03-08 09:35:04 UTC
Based on comment 28 and sanity testing switching to verified.

Comment 33 errata-xmlrpc 2021-03-16 13:48:42 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory (grub2 bug fix and enhancement update), and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://access.redhat.com/errata/RHBA-2021:0852