limine: Add support for framebuffer modes listing

This commit is contained in:
mintsuki 2022-10-14 05:17:24 +02:00
parent 01ee09373c
commit e0aec79cb6
10 changed files with 233 additions and 23 deletions

View File

@ -580,6 +580,28 @@ struct limine_framebuffer {
uint8_t unused[7]; uint8_t unused[7];
uint64_t edid_size; uint64_t edid_size;
void *edid; void *edid;
/* Revision 1 */
uint64_t mode_count;
struct limine_video_mode **modes;
};
`modes` is an array of `mode_count` pointers to `struct limine_video_mode` describing the
available video modes for the given framebuffer.
```
struct limine_video_mode {
uint64_t pitch;
uint64_t width;
uint64_t height;
uint16_t bpp;
uint8_t memory_model;
uint8_t red_mask_size;
uint8_t red_mask_shift;
uint8_t green_mask_size;
uint8_t green_mask_shift;
uint8_t blue_mask_size;
uint8_t blue_mask_shift;
}; };
``` ```

View File

@ -37,19 +37,19 @@ static void linear_mask_to_mask_shift(
// Most of this code taken from https://wiki.osdev.org/GOP // Most of this code taken from https://wiki.osdev.org/GOP
bool gop_force_16 = false; static bool mode_to_fb_info(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, struct fb_info *ret, size_t mode) {
static bool try_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop,
struct fb_info *ret, size_t mode, uint64_t width, uint64_t height, int bpp) {
EFI_STATUS status; EFI_STATUS status;
ret->default_res = false;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mode_info; EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mode_info;
UINTN mode_info_size; UINTN mode_info_size;
status = gop->QueryMode(gop, mode, &mode_info_size, &mode_info); status = gop->QueryMode(gop, mode, &mode_info_size, &mode_info);
if (status) if (status) {
return false; return false;
}
switch (mode_info->PixelFormat) { switch (mode_info->PixelFormat) {
case PixelBlueGreenRedReserved8BitPerColor: case PixelBlueGreenRedReserved8BitPerColor:
@ -87,20 +87,39 @@ static bool try_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop,
mode_info->PixelInformation.BlueMask); mode_info->PixelInformation.BlueMask);
break; break;
default: default:
panic(false, "gop: Invalid PixelFormat");
}
if (width != 0 && height != 0 && bpp != 0) {
if ((uint64_t)mode_info->HorizontalResolution != width
|| (uint64_t)mode_info->VerticalResolution != height
|| (int)ret->framebuffer_bpp != bpp)
return false; return false;
} }
ret->memory_model = 0x06;
ret->framebuffer_pitch = mode_info->PixelsPerScanLine * (ret->framebuffer_bpp / 8);
ret->framebuffer_width = mode_info->HorizontalResolution;
ret->framebuffer_height = mode_info->VerticalResolution;
return true;
}
bool gop_force_16 = false;
static bool try_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop,
struct fb_info *ret, size_t mode, uint64_t width, uint64_t height, int bpp) {
EFI_STATUS status;
if (!mode_to_fb_info(gop, ret, mode)) {
return false;
}
if (width != 0 && height != 0 && bpp != 0) {
if (ret->framebuffer_width != width
|| ret->framebuffer_height != height
|| ret->framebuffer_bpp != bpp) {
return false;
}
}
if (gop_force_16) { if (gop_force_16) {
if (mode_info->HorizontalResolution >= 65536 if (ret->framebuffer_width >= 65536
|| mode_info->VerticalResolution >= 65536 || ret->framebuffer_height >= 65536
|| mode_info->PixelsPerScanLine * (ret->framebuffer_bpp / 8) >= 65536) { || ret->framebuffer_pitch >= 65536) {
return false; return false;
} }
} }
@ -121,17 +140,67 @@ static bool try_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop,
current_video_mode = mode; current_video_mode = mode;
ret->memory_model = 0x06;
ret->framebuffer_addr = gop->Mode->FrameBufferBase; ret->framebuffer_addr = gop->Mode->FrameBufferBase;
ret->framebuffer_pitch = gop->Mode->Info->PixelsPerScanLine * (ret->framebuffer_bpp / 8);
ret->framebuffer_width = gop->Mode->Info->HorizontalResolution;
ret->framebuffer_height = gop->Mode->Info->VerticalResolution;
fb_clear(ret); fb_clear(ret);
return true; return true;
} }
struct fb_info *gop_get_mode_list(size_t *count) {
EFI_STATUS status;
EFI_HANDLE tmp_handles[1];
EFI_HANDLE *handles = tmp_handles;
UINTN handles_size = sizeof(EFI_HANDLE);
EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
status = gBS->LocateHandle(ByProtocol, &gop_guid, NULL, &handles_size, handles);
if (status != EFI_SUCCESS && status != EFI_BUFFER_TOO_SMALL) {
return false;
}
handles = ext_mem_alloc(handles_size);
status = gBS->LocateHandle(ByProtocol, &gop_guid, NULL, &handles_size, handles);
if (status != EFI_SUCCESS) {
pmm_free(handles, handles_size);
return false;
}
EFI_HANDLE gop_handle = handles[0];
pmm_free(handles, handles_size);
EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
status = gBS->HandleProtocol(gop_handle, &gop_guid, (void **)&gop);
if (status != EFI_SUCCESS) {
return false;
}
UINTN modes_count = gop->Mode->MaxMode;
struct fb_info *ret = ext_mem_alloc(modes_count * sizeof(struct fb_info));
size_t actual_count = 0;
for (size_t i = 0; i < modes_count; i++) {
if (mode_to_fb_info(gop, &ret[actual_count], i)) {
actual_count++;
}
}
struct fb_info *tmp = ext_mem_alloc(actual_count * sizeof(struct fb_info));
memcpy(tmp, ret, actual_count * sizeof(struct fb_info));
pmm_free(ret, modes_count * sizeof(struct fb_info));
ret = tmp;
*count = modes_count;
return ret;
}
#define INVALID_PRESET_MODE 0xffffffff #define INVALID_PRESET_MODE 0xffffffff
static no_unwind size_t preset_mode = INVALID_PRESET_MODE; static no_unwind size_t preset_mode = INVALID_PRESET_MODE;

View File

@ -4,6 +4,7 @@
#if defined (UEFI) #if defined (UEFI)
#include <stdint.h> #include <stdint.h>
#include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
#include <efi.h> #include <efi.h>
#include <lib/fb.h> #include <lib/fb.h>
@ -11,6 +12,8 @@
bool init_gop(struct fb_info *ret, bool init_gop(struct fb_info *ret,
uint64_t target_width, uint64_t target_height, uint16_t target_bpp); uint64_t target_width, uint64_t target_height, uint16_t target_bpp);
struct fb_info *gop_get_mode_list(size_t *count);
extern bool gop_force_16; extern bool gop_force_16;
#endif #endif

View File

@ -116,6 +116,73 @@ static int set_vbe_mode(uint16_t mode) {
return r.eax & 0xff; return r.eax & 0xff;
} }
struct fb_info *vbe_get_mode_list(size_t *count) {
struct vbe_info_struct vbe_info;
get_vbe_info(&vbe_info);
uint16_t *vid_modes = (uint16_t *)rm_desegment(vbe_info.vid_modes_seg,
vbe_info.vid_modes_off);
size_t modes_count = 0;
for (size_t i = 0; vid_modes[i] != 0xffff; i++) {
struct vbe_mode_info_struct vbe_mode_info;
get_vbe_mode_info(&vbe_mode_info, vid_modes[i]);
// We only support RGB for now
if (vbe_mode_info.memory_model != 0x06)
continue;
// We only support linear modes
if (!(vbe_mode_info.mode_attributes & (1 << 7)))
continue;
modes_count++;
}
struct fb_info *ret = ext_mem_alloc(modes_count * sizeof(struct fb_info));
for (size_t i = 0, j = 0; vid_modes[i] != 0xffff; i++) {
struct vbe_mode_info_struct vbe_mode_info;
get_vbe_mode_info(&vbe_mode_info, vid_modes[i]);
// We only support RGB for now
if (vbe_mode_info.memory_model != 0x06)
continue;
// We only support linear modes
if (!(vbe_mode_info.mode_attributes & (1 << 7)))
continue;
ret[j].memory_model = vbe_mode_info.memory_model;
ret[j].framebuffer_width = vbe_mode_info.res_x;
ret[j].framebuffer_height = vbe_mode_info.res_y;
ret[j].framebuffer_bpp = vbe_mode_info.bpp;
if (vbe_info.version_maj < 3) {
ret[j].framebuffer_pitch = vbe_mode_info.bytes_per_scanline;
ret[j].red_mask_size = vbe_mode_info.red_mask_size;
ret[j].red_mask_shift = vbe_mode_info.red_mask_shift;
ret[j].green_mask_size = vbe_mode_info.green_mask_size;
ret[j].green_mask_shift = vbe_mode_info.green_mask_shift;
ret[j].blue_mask_size = vbe_mode_info.blue_mask_size;
ret[j].blue_mask_shift = vbe_mode_info.blue_mask_shift;
} else {
ret[j].framebuffer_pitch = vbe_mode_info.lin_bytes_per_scanline;
ret[j].red_mask_size = vbe_mode_info.lin_red_mask_size;
ret[j].red_mask_shift = vbe_mode_info.lin_red_mask_shift;
ret[j].green_mask_size = vbe_mode_info.lin_green_mask_size;
ret[j].green_mask_shift = vbe_mode_info.lin_green_mask_shift;
ret[j].blue_mask_size = vbe_mode_info.lin_blue_mask_size;
ret[j].blue_mask_shift = vbe_mode_info.lin_blue_mask_shift;
}
j++;
}
*count = modes_count;
return ret;
}
bool init_vbe(struct fb_info *ret, bool init_vbe(struct fb_info *ret,
uint16_t target_width, uint16_t target_height, uint16_t target_bpp) { uint16_t target_width, uint16_t target_height, uint16_t target_bpp) {
printv("vbe: Initialising...\n"); printv("vbe: Initialising...\n");

View File

@ -2,10 +2,13 @@
#define __DRIVERS__VBE_H__ #define __DRIVERS__VBE_H__
#include <stdint.h> #include <stdint.h>
#include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
#include <lib/fb.h> #include <lib/fb.h>
bool init_vbe(struct fb_info *ret, bool init_vbe(struct fb_info *ret,
uint16_t target_width, uint16_t target_height, uint16_t target_bpp); uint16_t target_width, uint16_t target_height, uint16_t target_bpp);
struct fb_info *vbe_get_mode_list(size_t *count);
#endif #endif

View File

@ -19,6 +19,14 @@ bool fb_init(struct fb_info *ret,
return r; return r;
} }
struct fb_info *fb_get_mode_list(size_t *count) {
#if defined (BIOS)
return vbe_get_mode_list(count);
#elif defined (UEFI)
return gop_get_mode_list(count);
#endif
}
void fb_clear(struct fb_info *fb) { void fb_clear(struct fb_info *fb) {
for (size_t y = 0; y < fb->framebuffer_height; y++) { for (size_t y = 0; y < fb->framebuffer_height; y++) {
switch (fb->framebuffer_bpp) { switch (fb->framebuffer_bpp) {

View File

@ -2,6 +2,7 @@
#define __LIB__FB_H__ #define __LIB__FB_H__
#include <stdint.h> #include <stdint.h>
#include <stddef.h>
struct resolution { struct resolution {
uint64_t width; uint64_t width;
@ -10,24 +11,27 @@ struct resolution {
}; };
struct fb_info { struct fb_info {
bool default_res;
uint8_t memory_model;
uint64_t framebuffer_addr;
uint64_t framebuffer_pitch; uint64_t framebuffer_pitch;
uint64_t framebuffer_width; uint64_t framebuffer_width;
uint64_t framebuffer_height; uint64_t framebuffer_height;
uint16_t framebuffer_bpp; uint16_t framebuffer_bpp;
uint8_t memory_model;
uint8_t red_mask_size; uint8_t red_mask_size;
uint8_t red_mask_shift; uint8_t red_mask_shift;
uint8_t green_mask_size; uint8_t green_mask_size;
uint8_t green_mask_shift; uint8_t green_mask_shift;
uint8_t blue_mask_size; uint8_t blue_mask_size;
uint8_t blue_mask_shift; uint8_t blue_mask_shift;
bool default_res;
uint64_t framebuffer_addr;
}; };
bool fb_init(struct fb_info *ret, bool fb_init(struct fb_info *ret,
uint64_t target_width, uint64_t target_height, uint16_t target_bpp); uint64_t target_width, uint64_t target_height, uint16_t target_bpp);
struct fb_info *fb_get_mode_list(size_t *count);
void fb_clear(struct fb_info *fb); void fb_clear(struct fb_info *fb);
#endif #endif

View File

@ -522,7 +522,6 @@ FEAT_START
smbios_request->response = reported_addr(smbios_response); smbios_request->response = reported_addr(smbios_response);
FEAT_END FEAT_END
#if defined (UEFI) #if defined (UEFI)
// EFI system table feature // EFI system table feature
FEAT_START FEAT_START
@ -791,6 +790,20 @@ FEAT_START
fbp->edid = reported_addr(edid_info); fbp->edid = reported_addr(edid_info);
} }
framebuffer_response->revision = 1;
size_t modes_count;
struct fb_info *modes = fb_get_mode_list(&modes_count);
if (modes != NULL) {
uint64_t *modes_list = ext_mem_alloc(modes_count * sizeof(uint64_t));
for (size_t i = 0; i < modes_count; i++) {
modes[i].memory_model = LIMINE_FRAMEBUFFER_RGB;
modes_list[i] = reported_addr(&modes[i]);
}
fbp->modes = reported_addr(modes_list);
fbp->mode_count = modes_count;
}
fbp->memory_model = LIMINE_FRAMEBUFFER_RGB; fbp->memory_model = LIMINE_FRAMEBUFFER_RGB;
fbp->address = reported_addr((void *)(uintptr_t)fb.framebuffer_addr); fbp->address = reported_addr((void *)(uintptr_t)fb.framebuffer_addr);
fbp->width = fb.framebuffer_width; fbp->width = fb.framebuffer_width;

View File

@ -97,6 +97,20 @@ struct limine_hhdm_request {
#define LIMINE_FRAMEBUFFER_RGB 1 #define LIMINE_FRAMEBUFFER_RGB 1
struct limine_video_mode {
uint64_t pitch;
uint64_t width;
uint64_t height;
uint16_t bpp;
uint8_t memory_model;
uint8_t red_mask_size;
uint8_t red_mask_shift;
uint8_t green_mask_size;
uint8_t green_mask_shift;
uint8_t blue_mask_size;
uint8_t blue_mask_shift;
};
struct limine_framebuffer { struct limine_framebuffer {
LIMINE_PTR(void *) address; LIMINE_PTR(void *) address;
uint64_t width; uint64_t width;
@ -113,6 +127,9 @@ struct limine_framebuffer {
uint8_t unused[7]; uint8_t unused[7];
uint64_t edid_size; uint64_t edid_size;
LIMINE_PTR(void *) edid; LIMINE_PTR(void *) edid;
/* Revision 1 */
uint64_t mode_count;
LIMINE_PTR(struct limine_video_mode **) modes;
}; };
struct limine_framebuffer_response { struct limine_framebuffer_response {

View File

@ -296,6 +296,10 @@ FEAT_START
e9_printf("Blue mask shift: %d", fb->blue_mask_shift); e9_printf("Blue mask shift: %d", fb->blue_mask_shift);
e9_printf("EDID size: %d", fb->edid_size); e9_printf("EDID size: %d", fb->edid_size);
e9_printf("EDID at: %x", fb->edid); e9_printf("EDID at: %x", fb->edid);
e9_printf("Video modes:");
for (size_t j = 0; j < fb->mode_count; j++) {
e9_printf(" %dx%dx%d", fb->modes[j]->width, fb->modes[j]->height, fb->modes[j]->bpp);
}
} }
FEAT_END FEAT_END