limine: Add support for framebuffer modes listing
This commit is contained in:
parent
01ee09373c
commit
e0aec79cb6
22
PROTOCOL.md
22
PROTOCOL.md
|
@ -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;
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
17
limine.h
17
limine.h
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue