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; uintn_t frame_buffer_size;
} efi_gop_mode_t; } efi_gop_mode_t;
typedef struct { typedef struct efi_graphics_output_s {
void *query_mode; efi_status_t (efiapi *query_mode)(struct efi_graphics_output_s *, uint32_t, uintn_t *, efi_gop_mode_info_t **);
void *set_mode; efi_status_t (efiapi *set_mode)(struct efi_graphics_output_s *, uint32_t);
void *blt; void *blt;
efi_gop_mode_t *mode; efi_gop_mode_t *mode;
} efi_graphics_output_protocol_t; } efi_graphics_output_t;
typedef struct { typedef struct {
uint64_t signature; uint64_t signature;

View File

@ -24,6 +24,9 @@
#define MAP_BUFFER_HEADROOM 8 // number of descriptors #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 // Private Variables
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -249,37 +252,61 @@ static void get_bit_range(uint32_t mask, uint8_t *pos, uint8_t *size)
*size = length; *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_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++) { for (int i = 0; i < efi_get_num_handles(handles_size); i++) {
efi_handle_t handle = efi_get_handle_at(handles, i); efi_handle_t handle = efi_get_handle_at(handles, i);
efi_graphics_output_protocol_t *current_gop = NULL; efi_graphics_output_t *gop = NULL;
status = efi_call_bs(handle_protocol, handle, &EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, (void **)&current_gop); status = efi_call_bs(handle_protocol, handle, &EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, (void **)&gop);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
continue; continue;
} }
void *con_out = NULL; efi_gop_mode_t *mode = efi_table_attr(gop, mode);
status = efi_call_bs(handle_protocol, handle, &EFI_CONSOLE_OUT_DEVICE_GUID, &con_out); efi_gop_mode_info_t *info = efi_table_attr(mode, info);
efi_gop_mode_t *current_mode = efi_table_attr(current_gop, mode); // BLT is not available after we call ExitBootServices().
efi_gop_mode_info_t *current_info = efi_table_attr(current_mode, info); 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 // Systems that use the UEFI Console Splitter may provide multiple GOP
// devices, not all of which are backed by real hardware. The workaround // 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 // is to search for a GOP implementing the ConOut protocol, and if one
// isn't found, to just fall back to the first GOP. // 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; void *con_out = NULL;
if (con_out) { status = efi_call_bs(handle_protocol, handle, &EFI_CONSOLE_OUT_DEVICE_GUID, &con_out);
break; 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 (!gop) {
#if DEBUG #if DEBUG
print_string("GOP not found\n"); print_string("GOP not found\n");
@ -287,15 +314,43 @@ static efi_status_t set_screen_info_from_gop(screen_info_t *si, efi_handle_t *ha
return EFI_NOT_FOUND; return EFI_NOT_FOUND;
} }
efi_gop_mode_t *mode = efi_table_attr(gop, mode); 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); efi_phys_addr_t lfb_base = efi_table_attr(mode, frame_buffer_base);
si->orig_video_isVGA = VIDEO_TYPE_EFI; si->orig_video_isVGA = VIDEO_TYPE_EFI;
si->lfb_width = info->h_resolution; si->lfb_width = best_info.h_resolution;
si->lfb_height = info->v_resolution; si->lfb_height = best_info.v_resolution;
si->lfb_base = lfb_base; si->lfb_base = lfb_base;
#ifdef __x86_64__ #ifdef __x86_64__
if (lfb_base >> 32) { 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 #endif
switch (info->pixel_format) { switch (best_info.pixel_format) {
case PIXEL_RGB_RESERVED_8BIT_PER_COLOR: case PIXEL_RGB_RESERVED_8BIT_PER_COLOR:
#if DEBUG #if DEBUG
print_string("RGB32 mode\n"); print_string("RGB32 mode\n");
#endif #endif
si->lfb_depth = 32; 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_size = 8;
si->red_pos = 0; si->red_pos = 0;
si->green_size = 8; 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"); print_string("BGR32 mode\n");
#endif #endif
si->lfb_depth = 32; 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_size = 8;
si->red_pos = 16; si->red_pos = 16;
si->green_size = 8; 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 #if DEBUG
print_string("Bit mask mode\n"); print_string("Bit mask mode\n");
#endif #endif
get_bit_range(info->pixel_info.red_mask, &si->red_pos, &si->red_size); get_bit_range(best_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(best_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(best_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.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_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; break;
default: default:
#if DEBUG #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(); wait_for_key();
#endif #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; return EFI_SUCCESS;
} }