diff --git a/PROTOCOL.md b/PROTOCOL.md index 5b656cf2..97cbf6c4 100644 --- a/PROTOCOL.md +++ b/PROTOCOL.md @@ -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; }; ``` diff --git a/common/drivers/gop.c b/common/drivers/gop.c index 18b8734f..4e0b8833 100644 --- a/common/drivers/gop.c +++ b/common/drivers/gop.c @@ -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; diff --git a/common/drivers/gop.h b/common/drivers/gop.h index bbc44207..3b8afe53 100644 --- a/common/drivers/gop.h +++ b/common/drivers/gop.h @@ -4,6 +4,7 @@ #if defined (UEFI) #include +#include #include #include #include @@ -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 diff --git a/common/drivers/vbe.c b/common/drivers/vbe.c index 7273f471..2ecae69d 100644 --- a/common/drivers/vbe.c +++ b/common/drivers/vbe.c @@ -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"); diff --git a/common/drivers/vbe.h b/common/drivers/vbe.h index a212e486..37b5e234 100644 --- a/common/drivers/vbe.h +++ b/common/drivers/vbe.h @@ -2,10 +2,13 @@ #define __DRIVERS__VBE_H__ #include +#include #include #include 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 diff --git a/common/lib/fb.c b/common/lib/fb.c index f291e01d..95057e1c 100644 --- a/common/lib/fb.c +++ b/common/lib/fb.c @@ -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) { diff --git a/common/lib/fb.h b/common/lib/fb.h index f2428b76..83c65170 100644 --- a/common/lib/fb.h +++ b/common/lib/fb.h @@ -2,6 +2,7 @@ #define __LIB__FB_H__ #include +#include 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 diff --git a/common/protos/limine.c b/common/protos/limine.c index 9a40aa30..979a5bc7 100644 --- a/common/protos/limine.c +++ b/common/protos/limine.c @@ -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; diff --git a/limine.h b/limine.h index 774fb0b5..a929f2c3 100644 --- a/limine.h +++ b/limine.h @@ -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 { diff --git a/test/limine.c b/test/limine.c index ded2abaa..27ec7b20 100644 --- a/test/limine.c +++ b/test/limine.c @@ -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