dcavalca / rpms / grub2

Forked from rpms/grub2 3 years ago
Clone

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

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