nalika / rpms / grub2

Forked from rpms/grub2 2 years ago
Clone

Blame SOURCES/0328-Add-at_keyboard_fallback_set-var-to-force-the-set-ma.patch

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