Root cause for the invisible GRUB menu (same text for Debian #1051832, #1088356 
and savannah #65503)

I traced this on an iMac10,1 (Apple EFI 1.1, GOP + UGA + ConsoleControl,
no Secure Boot) with grub-efi-amd64-signed 1+2.12+9+deb13u2. A small test
module read and set the ConsoleControl mode without any console output
and stored the result with save_env.

1. The firmware hands over in graphics mode, but efi_uga's module init
   (video/efi_uga.c, check_protocol) ends with an unconditional
   grub_efi_set_text_mode(1). all_video is built into the signed image,
   so the console is in text mode before grub.cfg runs. Loading the 94
   built-in modules one by one showed the switch at all_video (efi_uga).
2. grub_efi_console_output_fini() switches back to graphics mode on
   "terminal_output gfxterm" only if the console has set text mode
   itself, which happens on the first text output. Up to 2.06 the
   "Welcome to GRUB!" message did that; 2.12 no longer prints it on EFI
   (upstream 3e4cbbeca0ef), so nothing switches back, the console stays
   in text mode, and the menu gfxterm draws on the GOP framebuffer is
   invisible while GRUB keeps working blind.

This explains why any echo (even echo -n "") before "terminal_output
gfxterm" works around it, while "sleep 1" does not, and why it only hits
firmware that has the ConsoleControl protocol (older Apple EFI, some
old ThinkPads). Calling SetMode(graphics) before gfxterm, with no echo,
shows the menu; setting text mode after gfxterm, with the echo, hides it.

Tested fixes (2.12-9+deb13u2, images built with debian/build-efi-images,
booted through shim, against an unpatched control image of the same
build; each alone shows the full-screen menu on the iMac10,1):
  U: efi_uga restores the previous ConsoleControl mode after probing
     instead of forcing text mode.
  A: grub_efi_console_output_fini() calls grub_efi_set_text_mode(0)
     even if text mode was never set (a no-op without the protocol or
     if already in graphics mode).
Patches (against Debian 2.12-9+deb13u2) are attached.

Upstream note: master no longer has this code path. Commit ef6ee4ad4
(efi: Drop support for UGA Draw and Console Control protocols, Feb
2026) removes efi_uga.c and the ConsoleControl calls, so I did not send
the patches upstream. That commit is not in 2.14. I have not tested
master on this machine. Debian could carry U and/or A for 2.12.
--- a/grub-core/video/efi_uga.c
+++ b/grub-core/video/efi_uga.c
@@ -28,6 +28,7 @@
 #include <grub/efi/api.h>
 #include <grub/efi/efi.h>
 #include <grub/efi/uga_draw.h>
+#include <grub/efi/console_control.h>
 #include <grub/pci.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
@@ -175,6 +176,21 @@
   return ctx.found;
 }
 
+static grub_guid_t console_control_guid = GRUB_EFI_CONSOLE_CONTROL_GUID;
+
+/* Return 1 if the firmware console is (or is assumed to be) in text mode.  */
+static int
+console_is_text (void)
+{
+  grub_efi_console_control_protocol_t *c;
+  grub_efi_screen_mode_t mode;
+
+  c = grub_efi_locate_protocol (&console_control_guid, 0);
+  if (!c || c->get_mode (c, &mode, 0, 0) != GRUB_EFI_SUCCESS)
+    return 1;
+  return mode == GRUB_EFI_SCREEN_TEXT;
+}
+
 static int
 check_protocol (void)
 {
@@ -184,17 +200,20 @@
   if (c)
     {
       grub_uint32_t width, height, depth, rate, pixel;
-      int ret;
+      int ret, was_text;
 
       if (c->get_mode (c, &width, &height, &depth, &rate))
 	return 0;
 
+      was_text = console_is_text ();
       grub_efi_set_text_mode (0);
       pixel = RGB_MAGIC;
       c->blt (c, (struct grub_efi_uga_pixel *) &pixel,
 	      GRUB_EFI_UGA_VIDEO_FILL, 0, 0, 0, 0, 1, height, 0);
       ret = find_framebuf (&uga_fb, &uga_pitch);
-      grub_efi_set_text_mode (1);
+      /* Restore the previous mode instead of forcing text mode.  */
+      if (was_text)
+	grub_efi_set_text_mode (1);
 
       if (ret)
 	{
--- a/grub-core/term/efi/console.c
+++ b/grub-core/term/efi/console.c
@@ -446,7 +446,17 @@
 grub_efi_console_output_fini (struct grub_term_output *term)
 {
   if (text_mode != GRUB_TEXT_MODE_AVAILABLE)
-    return 0;
+    {
+      /*
+       * No text has been output, so text-mode was never set by us. But some
+       * firmware (e.g. old Apple EFI) starts in text-mode, which keeps the
+       * graphics drawn by the next terminal (gfxterm) invisible. Make sure
+       * the console is in graphics-mode when leaving it.
+       */
+      if (!grub_efi_is_finished)
+	grub_efi_set_text_mode (0);
+      return 0;
+    }
 
   grub_console_setcursor (term, 0);
   grub_efi_set_text_mode (0);

Reply via email to