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.

	Also-By: Vladimir Serbinenko <phcoder@gmail.com>
This commit is contained in:
Matthew Garrett 2012-03-04 00:48:21 +01:00 committed by Vladimir 'phcoder' Serbinenko
parent 32107ec02a
commit 3935dde2f2
5 changed files with 200 additions and 5 deletions

View file

@ -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.

View 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)
{
*datasize_out = datasize;
return data;
}
grub_free (data);
return NULL;

View file

@ -27,14 +27,22 @@
#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,

54
include/grub/efi/edid.h Normal file
View file

@ -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

View 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);