]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Use EDID on EFI.
authorMatthew Garrett <mjg@redhat.com>
Sat, 3 Mar 2012 23:48:21 +0000 (00:48 +0100)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sat, 3 Mar 2012 23:48:21 +0000 (00:48 +0100)
* grub-core/kern/efi/efi.c (grub_efi_get_variable): New argument
datasize_out.
* grub-core/video/efi_gop.c (check_protocol): Check that GOP has usable
modes. Set gop_handle.
(grub_video_gop_get_edid): New function.
(grub_gop_get_preferred_mode): Likewise.
(grub_video_gop_setup): Use grub_gop_get_preferred_mode.
(grub_video_efi_gop_adapter): Set .get_edid.
* include/grub/efi/edid.h: New file.
* include/grub/efi/efi.h (grub_efi_get_variable): Update proto.

Also-By: Vladimir Serbinenko <phcoder@gmail.com>
ChangeLog
grub-core/kern/efi/efi.c
grub-core/video/efi_gop.c
include/grub/efi/edid.h [new file with mode: 0644]
include/grub/efi/efi.h

index 36fc62209378b7a0be1ec1101608fe24f412c047..d4a34fc1dbef6a9a50164ddeb8babc008975bc9b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2012-03-03  Matthew Garrett  <mjg@redhat.com>
+2012-03-03  Vladimir Serbinenko  <phcoder@gmail.com>
+
+       Use EDID on EFI.
+
+       * grub-core/kern/efi/efi.c (grub_efi_get_variable): New argument
+       datasize_out.
+       * grub-core/video/efi_gop.c (check_protocol): Check that GOP has usable
+       modes. Set gop_handle.
+       (grub_video_gop_get_edid): New function.
+       (grub_gop_get_preferred_mode): Likewise.
+       (grub_video_gop_setup): Use grub_gop_get_preferred_mode.
+       (grub_video_efi_gop_adapter): Set .get_edid.
+       * include/grub/efi/edid.h: New file.
+       * include/grub/efi/efi.h (grub_efi_get_variable): Update proto.
+
 2012-03-03  Vladimir Serbinenko  <phcoder@gmail.com>
 
        * util/grub-install.in: Load efivars unconditionally.
index 2b0a8b489bfaf9a13ef5052ed143c8bae0923166..6f12c76180a2ca9890a07e1c00c3bd238dd197ab 100644 (file)
@@ -183,7 +183,8 @@ grub_efi_set_virtual_address_map (grub_efi_uintn_t memory_map_size,
 }
 
 void *
-grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid)
+grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid,
+                      grub_size_t *datasize_out)
 {
   grub_efi_status_t status;
   grub_efi_uintn_t datasize = 0;
@@ -192,6 +193,8 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid)
   void *data;
   grub_size_t len, len16;
 
+  *datasize_out = 0;
+
   len = grub_strlen (var);
   len16 = len * GRUB_MAX_UTF16_PER_UTF8;
   var16 = grub_malloc ((len16 + 1) * sizeof (var16[0]));
@@ -204,6 +207,9 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid)
 
   status = efi_call_5 (r->get_variable, var16, guid, NULL, &datasize, NULL);
 
+  if (!datasize)
+    return NULL;
+
   data = grub_malloc (datasize);
   if (!data)
     {
@@ -215,7 +221,10 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid)
   grub_free (var16);
 
   if (status == GRUB_EFI_SUCCESS)
-    return data;
+    {
+      *datasize_out = datasize;
+      return data;
+    }
 
   grub_free (data);
   return NULL;
index d14ae98d2390c87a33a440cee0aa0970c98b8ade..73c5d08e523c3b4d4dc7f1acc1de9af6892945e7 100644 (file)
 #include <grub/video_fb.h>
 #include <grub/efi/api.h>
 #include <grub/efi/efi.h>
+#include <grub/efi/edid.h>
 #include <grub/efi/graphics_output.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
 static grub_efi_guid_t graphics_output_guid = GRUB_EFI_GOP_GUID;
+static grub_efi_guid_t active_edid_guid = GRUB_EFI_EDID_ACTIVE_GUID;
+static grub_efi_guid_t discovered_edid_guid = GRUB_EFI_EDID_DISCOVERED_GUID;
+static grub_efi_guid_t efi_var_guid = GRUB_EFI_GLOBAL_VARIABLE_GUID;
 static struct grub_efi_gop *gop;
 static unsigned old_mode;
 static int restore_needed;
+static grub_efi_handle_t gop_handle;
+
+static int
+grub_video_gop_iterate (int (*hook) (const struct grub_video_mode_info *info));
 
 static struct
 {
@@ -47,9 +55,37 @@ static struct
 static int
 check_protocol (void)
 {
-  gop = grub_efi_locate_protocol (&graphics_output_guid, 0);
-  if (gop)
+  grub_efi_handle_t *handles;
+  grub_efi_uintn_t num_handles, i;
+  int have_usable_mode = 0;
+
+  auto int hook (const struct grub_video_mode_info *info);
+  int hook (const struct grub_video_mode_info *info __attribute__ ((unused)))
+  {
+    have_usable_mode = 1;
     return 1;
+  }
+
+  handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL,
+                                   &graphics_output_guid, NULL, &num_handles);
+  if (!handles || num_handles == 0)
+    return 0;
+
+  for (i = 0; i < num_handles; i++)
+    {
+      gop_handle = handles[i];
+      gop = grub_efi_open_protocol (gop_handle, &graphics_output_guid,
+                                   GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+      grub_video_gop_iterate (hook);
+      if (have_usable_mode)
+       {
+         grub_free (handles);
+         return 1;
+       }
+    }
+
+  gop = 0;
+  gop_handle = 0;
 
   return 0;
 }
@@ -220,6 +256,64 @@ grub_video_gop_iterate (int (*hook) (const struct grub_video_mode_info *info))
   return 0;
 }
 
+static grub_err_t
+grub_video_gop_get_edid (struct grub_video_edid_info *edid_info)
+{
+  struct grub_efi_active_edid *edid;
+  grub_size_t copy_size;
+
+  grub_memset (edid_info, 0, sizeof (*edid_info));
+
+  edid = grub_efi_open_protocol (gop_handle, &active_edid_guid,
+                                GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+  if (!edid || edid->size_of_edid == 0)
+    edid = grub_efi_open_protocol (gop_handle, &discovered_edid_guid,
+                                  GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+  if (!edid || edid->size_of_edid == 0)
+    {
+      char edidname[] = "agp-internal-edid";
+      grub_size_t datasize;
+      grub_uint8_t *data;
+      data = grub_efi_get_variable (edidname, &efi_var_guid, &datasize);
+      if (data && datasize > 16)
+       {
+         copy_size = datasize - 16;
+         if (copy_size > sizeof (*edid_info))
+           copy_size = sizeof (*edid_info);
+         grub_memcpy (edid_info, data + 16, copy_size);
+         grub_free (data);
+         return GRUB_ERR_NONE;
+       }
+      return grub_error (GRUB_ERR_BAD_DEVICE, "EDID information not available");
+    }
+
+  copy_size = edid->size_of_edid;
+  if (copy_size > sizeof (*edid_info))
+    copy_size = sizeof (*edid_info);
+  grub_memcpy (edid_info, edid->edid, copy_size);
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_gop_get_preferred_mode (unsigned int *width, unsigned int *height)
+{
+  struct grub_video_edid_info edid_info;
+  grub_err_t err;
+
+  err = grub_video_gop_get_edid (&edid_info);
+  if (err)
+    return err;
+  err = grub_video_edid_checksum (&edid_info);
+  if (err)
+    return err;
+  err = grub_video_edid_preferred_mode (&edid_info, width, height);
+  if (err)
+    return err;
+  return GRUB_ERR_NONE;
+}
+
 static grub_err_t
 grub_video_gop_setup (unsigned int width, unsigned int height,
                      unsigned int mode_type,
@@ -232,10 +326,23 @@ grub_video_gop_setup (unsigned int width, unsigned int height,
   unsigned bpp;
   int found = 0;
   unsigned long long best_volume = 0;
+  unsigned int preferred_width = 0, preferred_height = 0;
 
   depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK)
     >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS;
 
+  if (width == 0 && height == 0)
+    {
+      err = 1;
+      grub_gop_get_preferred_mode (&preferred_width, &preferred_height);
+      if (err)
+       {
+         preferred_width = 800;
+         preferred_height = 600;
+         grub_errno = GRUB_ERR_NONE;
+       }
+    }
+
   /* Keep current mode if possible.  */
   if (gop->mode->info)
     {
@@ -270,6 +377,13 @@ grub_video_gop_setup (unsigned int width, unsigned int height,
          grub_dprintf ("video", "GOP: mode %d: %dx%d\n", mode, info->width,
                        info->height);
 
+         if (preferred_width && (info->width > preferred_width
+                                 || info->height > preferred_height))
+           {
+             grub_dprintf ("video", "GOP: mode %d: too large\n", mode);
+             continue;
+           }
+
          bpp = grub_video_gop_get_bpp (info);
          if (!bpp)
            {
@@ -401,6 +515,7 @@ static struct grub_video_adapter grub_video_gop_adapter =
     .setup = grub_video_gop_setup,
     .get_info = grub_video_fb_get_info,
     .get_info_and_fini = grub_video_gop_get_info_and_fini,
+    .get_edid = grub_video_gop_get_edid,
     .set_palette = grub_video_fb_set_palette,
     .get_palette = grub_video_fb_get_palette,
     .set_viewport = grub_video_fb_set_viewport,
diff --git a/include/grub/efi/edid.h b/include/grub/efi/edid.h
new file mode 100644 (file)
index 0000000..a0140b8
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2012  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_EFI_EDID_HEADER
+#define GRUB_EFI_EDID_HEADER   1
+
+/* Based on UEFI specification.  */
+
+#define GRUB_EFI_EDID_ACTIVE_GUID \
+  { 0xbd8c1056, 0x9f36, 0x44ec, { 0x92, 0xa8, 0xa6, 0x33, 0x7f, 0x81, 0x79, 0x86 }}
+
+#define GRUB_EFI_EDID_DISCOVERED_GUID \
+  {0x1c0c34f6,0xd380,0x41fa, {0xa0,0x49,0x8a,0xd0,0x6c,0x1a,0x66,0xaa}}
+
+#define GRUB_EFI_EDID_OVERRIDE_GUID \
+  {0x48ecb431,0xfb72,0x45c0, {0xa9,0x22,0xf4,0x58,0xfe,0x4,0xb,0xd5}}
+
+struct grub_efi_edid_override;
+
+typedef grub_efi_status_t
+(*grub_efi_edid_override_get_edid) (struct grub_efi_edid_override *this,
+                                   grub_efi_handle_t *childhandle,
+                                   grub_efi_uint32_t *attributes,
+                                   grub_efi_uintn_t *edidsize,
+                                   grub_efi_uint8_t *edid);
+struct grub_efi_edid_override {
+  grub_efi_edid_override_get_edid get_edid;
+};
+
+typedef struct grub_efi_edid_override grub_efi_edid_override_t;
+  
+
+struct grub_efi_active_edid
+{
+  grub_uint32_t size_of_edid;
+  grub_uint8_t *edid;
+};
+
+#endif
index 067e3bdacc22113c24fd10469a97614548f335c2..a1a28cd4ef38fae7c8ecdb5180aedcaea30ca0e1 100644 (file)
@@ -62,7 +62,8 @@ grub_err_t EXPORT_FUNC (grub_efi_set_virtual_address_map) (grub_efi_uintn_t memo
                                                           grub_efi_uint32_t descriptor_version,
                                                           grub_efi_memory_descriptor_t *virtual_map);
 void *EXPORT_FUNC (grub_efi_get_variable) (const char *variable,
-                                          const grub_efi_guid_t *guid);
+                                          const grub_efi_guid_t *guid,
+                                          grub_size_t *datasize_out);
 int
 EXPORT_FUNC (grub_efi_compare_device_paths) (const grub_efi_device_path_t *dp1,
                                             const grub_efi_device_path_t *dp2);