Select the best mode for the EFI framebuffer.

This chooses the lowest resolution that supports our main display.
This commit is contained in:
Martin Whitaker 2022-04-13 14:30:23 +01:00
parent 2cd060b22c
commit 6e9bdce92d
2 changed files with 92 additions and 29 deletions

View File

@ -123,12 +123,12 @@ typedef struct {
uintn_t frame_buffer_size;
} efi_gop_mode_t;
typedef struct {
void *query_mode;
void *set_mode;
typedef struct efi_graphics_output_s {
efi_status_t (efiapi *query_mode)(struct efi_graphics_output_s *, uint32_t, uintn_t *, efi_gop_mode_info_t **);
efi_status_t (efiapi *set_mode)(struct efi_graphics_output_s *, uint32_t);
void *blt;
efi_gop_mode_t *mode;
} efi_graphics_output_protocol_t;
} efi_graphics_output_t;
typedef struct {
uint64_t signature;

View File

@ -24,6 +24,9 @@
#define MAP_BUFFER_HEADROOM 8 // number of descriptors
#define MIN_H_RESOLUTION 640 // as required by our main display
#define MIN_V_RESOLUTION 400
//------------------------------------------------------------------------------
// Private Variables
//------------------------------------------------------------------------------
@ -249,37 +252,61 @@ static void get_bit_range(uint32_t mask, uint8_t *pos, uint8_t *size)
*size = length;
}
static efi_status_t set_screen_info_from_gop(screen_info_t *si, efi_handle_t *handles, size_t handles_size)
static efi_graphics_output_t *find_gop(efi_handle_t *handles, size_t handles_size)
{
efi_status_t status;
efi_graphics_output_protocol_t *gop = NULL;
efi_graphics_output_t *first_gop = NULL;
for (int i = 0; i < efi_get_num_handles(handles_size); i++) {
efi_handle_t handle = efi_get_handle_at(handles, i);
efi_graphics_output_protocol_t *current_gop = NULL;
status = efi_call_bs(handle_protocol, handle, &EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, (void **)&current_gop);
efi_graphics_output_t *gop = NULL;
status = efi_call_bs(handle_protocol, handle, &EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, (void **)&gop);
if (status != EFI_SUCCESS) {
continue;
}
void *con_out = NULL;
status = efi_call_bs(handle_protocol, handle, &EFI_CONSOLE_OUT_DEVICE_GUID, &con_out);
efi_gop_mode_t *mode = efi_table_attr(gop, mode);
efi_gop_mode_info_t *info = efi_table_attr(mode, info);
efi_gop_mode_t *current_mode = efi_table_attr(current_gop, mode);
efi_gop_mode_info_t *current_info = efi_table_attr(current_mode, info);
// BLT is not available after we call ExitBootServices().
if (info->pixel_format == PIXEL_BLT_ONLY) {
continue;
}
#if DEBUG
print_string("Found GOP with ");
print_dec(mode->max_mode);
print_string(" modes\n");
#endif
// Systems that use the UEFI Console Splitter may provide multiple GOP
// devices, not all of which are backed by real hardware. The workaround
// is to search for a GOP implementing the ConOut protocol, and if one
// isn't found, to just fall back to the first GOP.
if ((!gop || con_out) && current_info->pixel_format != PIXEL_BLT_ONLY) {
gop = current_gop;
if (con_out) {
break;
}
void *con_out = NULL;
status = efi_call_bs(handle_protocol, handle, &EFI_CONSOLE_OUT_DEVICE_GUID, &con_out);
if (status == EFI_SUCCESS) {
#if DEBUG
print_string("This GOP implements the ConOut protocol\n");
#endif
return gop;
}
if (first_gop == NULL) {
first_gop = gop;
}
}
return first_gop;
}
static efi_status_t set_screen_info_from_gop(screen_info_t *si, efi_handle_t *handles, size_t handles_size)
{
efi_status_t status;
efi_graphics_output_t *gop = find_gop(handles, handles_size);
if (!gop) {
#if DEBUG
print_string("GOP not found\n");
@ -288,14 +315,42 @@ static efi_status_t set_screen_info_from_gop(screen_info_t *si, efi_handle_t *ha
}
efi_gop_mode_t *mode = efi_table_attr(gop, mode);
efi_gop_mode_info_t *info = efi_table_attr(mode, info);
efi_gop_mode_info_t best_info;
best_info.h_resolution = UINT32_MAX;
best_info.v_resolution = UINT32_MAX;
uint32_t best_mode = UINT32_MAX;
for (uint32_t mode_num = 0; mode_num < mode->max_mode; mode_num++) {
efi_gop_mode_info_t *info;
uintn_t info_size;
status = efi_call_proto(gop, query_mode, mode_num, &info_size, &info);
if (status != EFI_SUCCESS) {
continue;
}
if (info->h_resolution >= MIN_H_RESOLUTION
&& info->v_resolution >= MIN_V_RESOLUTION
&& info->h_resolution < best_info.h_resolution) {
best_mode = mode_num;
best_info = *info;
}
efi_call_bs(free_pool, info);
}
if (best_mode == UINT32_MAX) {
#if DEBUG
print_string("No suitable GOP screen resolution\n");
#endif
return EFI_NOT_FOUND;
}
efi_phys_addr_t lfb_base = efi_table_attr(mode, frame_buffer_base);
si->orig_video_isVGA = VIDEO_TYPE_EFI;
si->lfb_width = info->h_resolution;
si->lfb_height = info->v_resolution;
si->lfb_width = best_info.h_resolution;
si->lfb_height = best_info.v_resolution;
si->lfb_base = lfb_base;
#ifdef __x86_64__
if (lfb_base >> 32) {
@ -304,13 +359,13 @@ static efi_status_t set_screen_info_from_gop(screen_info_t *si, efi_handle_t *ha
}
#endif
switch (info->pixel_format) {
switch (best_info.pixel_format) {
case PIXEL_RGB_RESERVED_8BIT_PER_COLOR:
#if DEBUG
print_string("RGB32 mode\n");
#endif
si->lfb_depth = 32;
si->lfb_linelength = info->pixels_per_scan_line * 4;
si->lfb_linelength = best_info.pixels_per_scan_line * 4;
si->red_size = 8;
si->red_pos = 0;
si->green_size = 8;
@ -325,7 +380,7 @@ static efi_status_t set_screen_info_from_gop(screen_info_t *si, efi_handle_t *ha
print_string("BGR32 mode\n");
#endif
si->lfb_depth = 32;
si->lfb_linelength = info->pixels_per_scan_line * 4;
si->lfb_linelength = best_info.pixels_per_scan_line * 4;
si->red_size = 8;
si->red_pos = 16;
si->green_size = 8;
@ -339,12 +394,12 @@ static efi_status_t set_screen_info_from_gop(screen_info_t *si, efi_handle_t *ha
#if DEBUG
print_string("Bit mask mode\n");
#endif
get_bit_range(info->pixel_info.red_mask, &si->red_pos, &si->red_size);
get_bit_range(info->pixel_info.green_mask, &si->green_pos, &si->green_size);
get_bit_range(info->pixel_info.blue_mask, &si->blue_pos, &si->blue_size);
get_bit_range(info->pixel_info.rsvd_mask, &si->rsvd_pos, &si->rsvd_size);
get_bit_range(best_info.pixel_info.red_mask, &si->red_pos, &si->red_size);
get_bit_range(best_info.pixel_info.green_mask, &si->green_pos, &si->green_size);
get_bit_range(best_info.pixel_info.blue_mask, &si->blue_pos, &si->blue_size);
get_bit_range(best_info.pixel_info.rsvd_mask, &si->rsvd_pos, &si->rsvd_size);
si->lfb_depth = si->red_size + si->green_size + si->blue_size + si->rsvd_size;
si->lfb_linelength = (info->pixels_per_scan_line * si->lfb_depth) / 8;
si->lfb_linelength = (best_info.pixels_per_scan_line * si->lfb_depth) / 8;
break;
default:
#if DEBUG
@ -386,6 +441,14 @@ static efi_status_t set_screen_info_from_gop(screen_info_t *si, efi_handle_t *ha
wait_for_key();
#endif
status = efi_call_proto(gop, set_mode, best_mode);
if (status != EFI_SUCCESS) {
#if DEBUG
print_string("Set GOP mode failed\n");
#endif
return status;
}
return EFI_SUCCESS;
}