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];
uint64_t edid_size;
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
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) {
static bool mode_to_fb_info(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop, struct fb_info *ret, size_t mode) {
EFI_STATUS status;
ret->default_res = false;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mode_info;
UINTN mode_info_size;
status = gop->QueryMode(gop, mode, &mode_info_size, &mode_info);
if (status)
if (status) {
return false;
}
switch (mode_info->PixelFormat) {
case PixelBlueGreenRedReserved8BitPerColor:
@ -87,20 +87,39 @@ static bool try_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop,
mode_info->PixelInformation.BlueMask);
break;
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;
}
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 (mode_info->HorizontalResolution >= 65536
|| mode_info->VerticalResolution >= 65536
|| mode_info->PixelsPerScanLine * (ret->framebuffer_bpp / 8) >= 65536) {
if (ret->framebuffer_width >= 65536
|| ret->framebuffer_height >= 65536
|| ret->framebuffer_pitch >= 65536) {
return false;
}
}
@ -121,17 +140,67 @@ static bool try_mode(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop,
current_video_mode = mode;
ret->memory_model = 0x06;
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);
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
static no_unwind size_t preset_mode = INVALID_PRESET_MODE;

View File

@ -4,6 +4,7 @@
#if defined (UEFI)
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <efi.h>
#include <lib/fb.h>
@ -11,6 +12,8 @@
bool init_gop(struct fb_info *ret,
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;
#endif

View File

@ -116,6 +116,73 @@ static int set_vbe_mode(uint16_t mode) {
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,
uint16_t target_width, uint16_t target_height, uint16_t target_bpp) {
printv("vbe: Initialising...\n");

View File

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

View File

@ -19,6 +19,14 @@ bool fb_init(struct fb_info *ret,
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) {
for (size_t y = 0; y < fb->framebuffer_height; y++) {
switch (fb->framebuffer_bpp) {

View File

@ -2,6 +2,7 @@
#define __LIB__FB_H__
#include <stdint.h>
#include <stddef.h>
struct resolution {
uint64_t width;
@ -10,24 +11,27 @@ struct resolution {
};
struct fb_info {
bool default_res;
uint8_t memory_model;
uint64_t framebuffer_addr;
uint64_t framebuffer_pitch;
uint64_t framebuffer_width;
uint64_t framebuffer_height;
uint16_t framebuffer_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;
bool default_res;
uint64_t framebuffer_addr;
};
bool fb_init(struct fb_info *ret,
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);
#endif

View File

@ -522,7 +522,6 @@ FEAT_START
smbios_request->response = reported_addr(smbios_response);
FEAT_END
#if defined (UEFI)
// EFI system table feature
FEAT_START
@ -791,6 +790,20 @@ FEAT_START
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->address = reported_addr((void *)(uintptr_t)fb.framebuffer_addr);
fbp->width = fb.framebuffer_width;

View File

@ -97,6 +97,20 @@ struct limine_hhdm_request {
#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 {
LIMINE_PTR(void *) address;
uint64_t width;
@ -113,6 +127,9 @@ struct limine_framebuffer {
uint8_t unused[7];
uint64_t edid_size;
LIMINE_PTR(void *) edid;
/* Revision 1 */
uint64_t mode_count;
LIMINE_PTR(struct limine_video_mode **) modes;
};
struct limine_framebuffer_response {

View File

@ -296,6 +296,10 @@ FEAT_START
e9_printf("Blue mask shift: %d", fb->blue_mask_shift);
e9_printf("EDID size: %d", fb->edid_size);
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