Fix keyboards that report IBM PC AT scan codes

Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
This commit is contained in:
Javier Martinez Canillas 2021-03-05 10:26:42 +01:00
parent 32351b3093
commit 3b8cfc9cf6
No known key found for this signature in database
GPG key ID: C751E590D63F3D69
4 changed files with 366 additions and 1 deletions

View file

@ -0,0 +1,121 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
Date: Thu, 3 Dec 2020 09:13:24 +0100
Subject: [PATCH] at_keyboard: use set 1 when keyboard is in Translate mode
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
When keyboard controller acts in Translate mode (0x40 mask), then use
set 1 since translation is done.
Otherwise use the mode queried from the controller (usually set 2).
Added "atkeyb" debugging messages in at_keyboard module as well.
Resolves: rhbz#1897587
Tested on:
- Asus N53SN (set 1 used)
- Dell Precision (set 1 used)
- HP Elitebook (set 2 used)
- HP G5430 (set 1 used, keyboard in XT mode!)
- Lenovo P71 & Lenovo T460s (set 2 used)
- QEMU/KVM (set 1 used)
Signed-off-by: Renaud Métrich <rmetrich@redhat.com>
---
grub-core/term/at_keyboard.c | 29 ++++++++++++++++++++++++-----
include/grub/at_keyboard.h | 4 ++++
2 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/grub-core/term/at_keyboard.c b/grub-core/term/at_keyboard.c
index f0a986eb176..69d99b61df5 100644
--- a/grub-core/term/at_keyboard.c
+++ b/grub-core/term/at_keyboard.c
@@ -135,20 +135,28 @@ query_mode (void)
int e;
e = write_mode (0);
- if (!e)
+ if (!e) {
+ grub_dprintf("atkeyb", "query_mode: write_mode(0) failed\n");
return 0;
+ }
do {
keyboard_controller_wait_until_ready ();
ret = grub_inb (KEYBOARD_REG_DATA);
} while (ret == GRUB_AT_ACK);
/* QEMU translates the set even in no-translate mode. */
- if (ret == 0x43 || ret == 1)
+ if (ret == 0x43 || ret == 1) {
+ grub_dprintf("atkeyb", "query_mode: returning 1 (ret=0x%x)\n", ret);
return 1;
- if (ret == 0x41 || ret == 2)
+ }
+ if (ret == 0x41 || ret == 2) {
+ grub_dprintf("atkeyb", "query_mode: returning 2 (ret=0x%x)\n", ret);
return 2;
- if (ret == 0x3f || ret == 3)
+ }
+ if (ret == 0x3f || ret == 3) {
+ grub_dprintf("atkeyb", "query_mode: returning 3 (ret=0x%x)\n", ret);
return 3;
+ }
return 0;
}
@@ -165,7 +173,13 @@ set_scancodes (void)
}
#if !USE_SCANCODE_SET
- ps2_state.current_set = 1;
+ if ((grub_keyboard_controller_orig & KEYBOARD_AT_TRANSLATE) == KEYBOARD_AT_TRANSLATE) {
+ grub_dprintf ("atkeyb", "queried set is %d but keyboard in Translate mode, so actually in set 1\n", grub_keyboard_orig_set);
+ ps2_state.current_set = 1;
+ } else {
+ grub_dprintf ("atkeyb", "using queried set %d\n", grub_keyboard_orig_set);
+ ps2_state.current_set = grub_keyboard_orig_set;
+ }
return;
#else
@@ -266,6 +280,7 @@ grub_keyboard_controller_init (void)
grub_keyboard_orig_set = 2;
#else
grub_keyboard_controller_orig = grub_keyboard_controller_read ();
+ grub_dprintf ("atkeyb", "grub_keyboard_controller_orig = 0x%x\n", grub_keyboard_controller_orig);
grub_keyboard_orig_set = query_mode ();
#endif
set_scancodes ();
@@ -275,11 +290,15 @@ grub_keyboard_controller_init (void)
static grub_err_t
grub_keyboard_controller_fini (struct grub_term_input *term __attribute__ ((unused)))
{
+/* In !USE_SCANCODE_SET mode, we didn't change anything, so nothing to restore */
+#if USE_SCANCODE_SET
if (ps2_state.current_set == 0)
return GRUB_ERR_NONE;
+ grub_dprintf ("atkeyb", "restoring set %d, controller 0x%x\n", grub_keyboard_orig_set, grub_keyboard_controller_orig);
if (grub_keyboard_orig_set)
write_mode (grub_keyboard_orig_set);
grub_keyboard_controller_write (grub_keyboard_controller_orig);
+#endif
return GRUB_ERR_NONE;
}
diff --git a/include/grub/at_keyboard.h b/include/grub/at_keyboard.h
index bcb4d9ba78f..9414dc1b996 100644
--- a/include/grub/at_keyboard.h
+++ b/include/grub/at_keyboard.h
@@ -19,6 +19,10 @@
#ifndef GRUB_AT_KEYBOARD_HEADER
#define GRUB_AT_KEYBOARD_HEADER 1
+/*
+ * Refer to https://wiki.osdev.org/%228042%22_PS/2_Controller for details.
+ */
+
/* Used for sending commands to the controller. */
#define KEYBOARD_COMMAND_ISREADY(x) !((x) & 0x02)
#define KEYBOARD_COMMAND_READ 0x20

View file

@ -0,0 +1,239 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
Date: Fri, 18 Dec 2020 15:39:26 +0100
Subject: [PATCH] Add 'at_keyboard_fallback_set' var to force the set manually
This seems required with HP DL380p Gen 8 systems.
Indeed, with this system, we can see the following sequence:
1. controller is queried to get current configuration (returns 0x30 which is quite standard)
2. controller is queried to get the current keyboard set in used, using code 0xf0 (first part)
3. controller answers with 0xfa which means "ACK" (== ok)
4. then we send "0" to tell "we want to know which set your are supporting"
5. controller answers with 0xfa ("ACK")
6. controller should then give us 1, 2, 3 or 0x43, 0x41, 0x3f, but here it gives us 0xfe which means "NACK"
Since there seems no way to determine the current set, and in fact the
controller expects set2 to be used, we need to rely on an environment
variable.
Everything has been tested on this system: using 0xFE (resend command),
making sure we wait for ACK in the 2 steps "write_mode", etc.
Below is litterature I used to come up with "there is no other
solution":
- https://wiki.osdev.org/%228042%22_PS/2_Controller
- http://www-ug.eecg.toronto.edu/msl/nios_devices/datasheets/PS2%20Keyboard%20Protocol.htm
- http://www.s100computers.com/My%20System%20Pages/MSDOS%20Board/PC%20Keyboard.pdf
---
grub-core/term/at_keyboard.c | 121 ++++++++++++++++++++++++++++++++++---------
1 file changed, 96 insertions(+), 25 deletions(-)
diff --git a/grub-core/term/at_keyboard.c b/grub-core/term/at_keyboard.c
index 69d99b61df5..c805cccbdde 100644
--- a/grub-core/term/at_keyboard.c
+++ b/grub-core/term/at_keyboard.c
@@ -31,6 +31,7 @@ GRUB_MOD_LICENSE ("GPLv3+");
static grub_uint8_t grub_keyboard_controller_orig;
static grub_uint8_t grub_keyboard_orig_set;
struct grub_ps2_state ps2_state;
+static int fallback_set;
static int ping_sent;
@@ -76,6 +77,8 @@ at_command (grub_uint8_t data)
break;
return 0;
}
+ if (i == GRUB_AT_TRIES)
+ grub_dprintf ("atkeyb", "at_command() timed out! (stopped after %d tries)\n", i);
return (i != GRUB_AT_TRIES);
}
@@ -105,6 +108,21 @@ grub_keyboard_controller_read (void)
#endif
+static int
+resend_last_result (void)
+{
+ grub_uint8_t ret;
+ keyboard_controller_wait_until_ready ();
+ grub_dprintf ("atkeyb", "resend_last_result: sending 0xfe\n");
+ grub_outb (0xfe, KEYBOARD_REG_DATA);
+ ret = wait_ack ();
+ grub_dprintf ("atkeyb", "resend_last_result: wait_ack() returned 0x%x\n", ret);
+ keyboard_controller_wait_until_ready ();
+ ret = grub_inb (KEYBOARD_REG_DATA);
+ grub_dprintf ("atkeyb", "resend_last_result: read 0x%x from controller\n", ret);
+ return ret;
+}
+
static int
write_mode (int mode)
{
@@ -113,11 +131,14 @@ write_mode (int mode)
{
grub_uint8_t ack;
keyboard_controller_wait_until_ready ();
+ grub_dprintf ("atkeyb", "write_mode: sending 0xf0\n");
grub_outb (0xf0, KEYBOARD_REG_DATA);
keyboard_controller_wait_until_ready ();
+ grub_dprintf ("atkeyb", "write_mode: sending mode %d\n", mode);
grub_outb (mode, KEYBOARD_REG_DATA);
keyboard_controller_wait_until_ready ();
ack = wait_ack ();
+ grub_dprintf ("atkeyb", "write_mode: wait_ack() returned 0x%x\n", ack);
if (ack == GRUB_AT_NACK)
continue;
if (ack == GRUB_AT_ACK)
@@ -125,6 +146,9 @@ write_mode (int mode)
return 0;
}
+ if (i == GRUB_AT_TRIES)
+ grub_dprintf ("atkeyb", "write_mode() timed out! (stopped after %d tries)\n", i);
+
return (i != GRUB_AT_TRIES);
}
@@ -132,31 +156,66 @@ static int
query_mode (void)
{
grub_uint8_t ret;
+ grub_uint64_t endtime;
+ unsigned i;
int e;
+ char *envvar;
- e = write_mode (0);
- if (!e) {
- grub_dprintf("atkeyb", "query_mode: write_mode(0) failed\n");
- return 0;
- }
+ for (i = 0; i < GRUB_AT_TRIES; i++) {
+ grub_dprintf ("atkeyb", "query_mode: sending command to controller\n");
+ e = write_mode (0);
+ if (!e) {
+ grub_dprintf ("atkeyb", "query_mode: write_mode(0) failed\n");
+ return 0;
+ }
- do {
- keyboard_controller_wait_until_ready ();
- ret = grub_inb (KEYBOARD_REG_DATA);
- } while (ret == GRUB_AT_ACK);
- /* QEMU translates the set even in no-translate mode. */
- if (ret == 0x43 || ret == 1) {
- grub_dprintf("atkeyb", "query_mode: returning 1 (ret=0x%x)\n", ret);
- return 1;
- }
- if (ret == 0x41 || ret == 2) {
- grub_dprintf("atkeyb", "query_mode: returning 2 (ret=0x%x)\n", ret);
- return 2;
+ endtime = grub_get_time_ms () + 20;
+ do {
+ keyboard_controller_wait_until_ready ();
+ ret = grub_inb (KEYBOARD_REG_DATA);
+ grub_dprintf ("atkeyb", "query_mode/loop: read 0x%x from controller\n", ret);
+ } while ((ret == GRUB_AT_ACK || ret == GRUB_AT_NACK) && grub_get_time_ms () < endtime);
+ if (ret == 0xfe) {
+ grub_dprintf ("atkeyb", "query_mode: asking controller to resend last result\n");
+ ret = resend_last_result();
+ grub_dprintf ("atkeyb", "query_mode: read 0x%x from controller\n", ret);
+ }
+ /* QEMU translates the set even in no-translate mode. */
+ if (ret == 0x43 || ret == 1) {
+ grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 1\n", ret);
+ return 1;
+ }
+ if (ret == 0x41 || ret == 2) {
+ grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 2\n", ret);
+ return 2;
+ }
+ if (ret == 0x3f || ret == 3) {
+ grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 3\n", ret);
+ return 3;
+ }
+ grub_dprintf ("atkeyb", "query_mode: controller returned unexpected value 0x%x, retrying\n", ret);
}
- if (ret == 0x3f || ret == 3) {
- grub_dprintf("atkeyb", "query_mode: returning 3 (ret=0x%x)\n", ret);
- return 3;
+
+ /*
+ * Falling here means we tried querying and the controller returned something
+ * we don't understand, try to use 'at_keyboard_fallback_set' if it exists,
+ * otherwise return 0.
+ */
+ envvar = grub_env_get ("at_keyboard_fallback_set");
+ if (envvar) {
+ fallback_set = grub_strtoul (envvar, 0, 10);
+ if ((grub_errno) || (fallback_set < 1) || (fallback_set > 3)) {
+ grub_dprintf ("atkeyb", "WARNING: ignoring unexpected value '%s' for '%s' variable\n",
+ envvar, "at_keyboard_fallback_set");
+ fallback_set = 0;
+ } else {
+ grub_dprintf ("atkeyb", "query_mode: '%s' specified in environment, returning %d\n",
+ "at_keyboard_fallback_set", fallback_set);
+ }
+ return fallback_set;
}
+ grub_dprintf ("atkeyb", "WARNING: no '%s' specified in environment, returning 0\n",
+ "at_keyboard_fallback_set");
return 0;
}
@@ -165,14 +224,25 @@ set_scancodes (void)
{
/* You must have visited computer museum. Keyboard without scancode set
knowledge. Assume XT. */
- if (!grub_keyboard_orig_set)
- {
- grub_dprintf ("atkeyb", "No sets support assumed\n");
- ps2_state.current_set = 1;
+ if (!grub_keyboard_orig_set) {
+ if (fallback_set) {
+ grub_dprintf ("atkeyb", "No sets support assumed but set forced to %d\n", fallback_set);
+ ps2_state.current_set = fallback_set;
return;
}
+ grub_dprintf ("atkeyb", "No sets support assumed, forcing to set 1\n");
+ ps2_state.current_set = 1;
+ return;
+ }
#if !USE_SCANCODE_SET
+ if (fallback_set) {
+ grub_dprintf ("atkeyb", "queried set is %d but set forced to %d\n",
+ grub_keyboard_orig_set, fallback_set);
+ ps2_state.current_set = fallback_set;
+ return;
+ }
+
if ((grub_keyboard_controller_orig & KEYBOARD_AT_TRANSLATE) == KEYBOARD_AT_TRANSLATE) {
grub_dprintf ("atkeyb", "queried set is %d but keyboard in Translate mode, so actually in set 1\n", grub_keyboard_orig_set);
ps2_state.current_set = 1;
@@ -261,6 +331,7 @@ grub_at_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused)))
static void
grub_keyboard_controller_init (void)
{
+ grub_dprintf ("atkeyb", "initializing the controller\n");
ps2_state.at_keyboard_status = 0;
/* Drain input buffer. */
while (1)
@@ -282,6 +353,7 @@ grub_keyboard_controller_init (void)
grub_keyboard_controller_orig = grub_keyboard_controller_read ();
grub_dprintf ("atkeyb", "grub_keyboard_controller_orig = 0x%x\n", grub_keyboard_controller_orig);
grub_keyboard_orig_set = query_mode ();
+ grub_dprintf ("atkeyb", "grub_keyboard_orig_set = %d\n", grub_keyboard_orig_set);
#endif
set_scancodes ();
keyboard_controller_led (ps2_state.led_status);
@@ -329,7 +401,6 @@ grub_at_restore_hw (void)
return GRUB_ERR_NONE;
}
-
static struct grub_term_input grub_at_keyboard_term =
{
.name = "at_keyboard",

View file

@ -299,3 +299,5 @@ Patch0298: 0298-appended-signatures-support-verifying-appended-signa.patch
Patch0299: 0299-appended-signatures-verification-tests.patch
Patch0300: 0300-appended-signatures-documentation.patch
Patch0301: 0301-ieee1275-link-appended-signature-enforcement-to-ibm-.patch
Patch0302: 0302-at_keyboard-use-set-1-when-keyboard-is-in-Translate-.patch
Patch0303: 0303-Add-at_keyboard_fallback_set-var-to-force-the-set-ma.patch

View file

@ -14,7 +14,7 @@
Name: grub2
Epoch: 1
Version: 2.04
Release: 37%{?dist}
Release: 38%{?dist}
Summary: Bootloader with support for Linux, Multiboot and more
License: GPLv3+
URL: http://www.gnu.org/software/grub/
@ -551,6 +551,9 @@ mv ${EFI_HOME}/grub.cfg.stb ${EFI_HOME}/grub.cfg
%endif
%changelog
* Fri Mar 05 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.04-38
- Fix keyboards that report IBM PC AT scan codes (rmetrich)
* Thu Feb 25 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.04-37
- Don't attempt to unify if there is no grub.cfg on EFI (gicmo)
Resolves: rhbz#1933085