#include "vesa.h" #include #include #include #include #include #include "base/types.h" #include "bios.h" #include "lib/boot.h" #include "tty.h" #include "x86/boot.h" #include "x86/regs.h" #include "x86/vga.h" #define VESA_LINEAR_FRAMEBUFFER (1 << 14) static bool fetch_vbe(vesa_info_t *buffer) { if (!buffer) { return true; } memcpy(buffer->signature, "VBE2", 4); regs32_t r = { 0 }; r.ax = 0x4f00; r.es = REAL_SEG(buffer); r.edi = REAL_OFFSET(buffer); bios_call(0x10, &r, &r); if (r.al != 0x4f || r.ah != 0x00) { return true; } return memcmp(buffer->signature, "VESA", 4) != 0; } static bool fetch_mode(vesa_mode_t *buffer, u16 mode) { if (!buffer) { return true; } regs32_t r = { 0 }; r.ax = 0x4f01; r.cx = mode; r.es = REAL_SEG(buffer); r.edi = REAL_OFFSET(buffer); bios_call(0x10, &r, &r); return r.al != 0x4f || r.ah != 0x00; } static bool fetch_edid(edid_data_t *buffer) { if (!buffer) { return true; } regs32_t r = { 0 }; r.ax = 0x4f15; r.bx = 0x01; r.es = REAL_SEG(buffer); r.edi = REAL_OFFSET(buffer); bios_call(0x10, &r, &r); return r.al != 0x4f || r.ah != 0; } static void edid_res(u8 *edid_data, edid_info_t *edid_info) { edid_info->monitor_width = edid_data[0x38] | ((int)(edid_data[0x3a] & 0xf0) << 4); edid_info->monitor_height = edid_data[0x3b] | ((int)(edid_data[0x3d] & 0xf0) << 4); } static bool set_mode(u16 mode_index) { regs32_t r = { 0 }; r.ax = 0x4f02; r.bx = mode_index | VESA_LINEAR_FRAMEBUFFER; bios_call(0x10, &r, &r); return r.al == 0x4f && r.ah == 0; } static vesa_mode_t pick_mode(u16 max_width, u16 max_height, u16 max_bpp) { vesa_info_t info_buffer = { 0 }; vesa_mode_t current_mode = { 0 }, best_mode = { 0 }; if (fetch_vbe(&info_buffer)) { return current_mode; } uintptr_t mode_list = REAL_FLATTEN(info_buffer.video_mode_seg, info_buffer.video_mode_off); if (mode_list < 0x0500 || mode_list > 0x10fff0) { return current_mode; } if (!max_width) { max_width = 0xffff; } if (!max_height) { max_height = 0xffff; } if (!max_bpp) { max_bpp = BOOT_DEFAULT_VESA_BPP; } u16 *mode_ptr = (u16 *)mode_list; u16 best_mode_i = 0; for (size_t i = 0; i < 1024; i++) { u16 mode = mode_ptr[i]; if (mode == 0xffff) { break; } if (fetch_mode(¤t_mode, mode)) { continue; } if (current_mode.memory_model != 0x06) { continue; } if ((current_mode.attributes & 0x90) != 0x90) { continue; } if (current_mode.width > max_width || current_mode.height > max_height || current_mode.bits_per_pixel > max_bpp) { continue; } if (current_mode.width > best_mode.width || current_mode.height > best_mode.height || current_mode.bits_per_pixel > best_mode.bits_per_pixel) { best_mode_i = mode; best_mode = current_mode; } } if (best_mode.bits_per_pixel && set_mode(best_mode_i)) { return best_mode; } return (vesa_mode_t){ 0 }; } static void text_video(video_info_t *video) { video->mode = VIDEO_TEXT; video->framebuffer = VGA_ADDR; video->width = VGA_WIDTH; video->height = VGA_HEIGHT; video->bytes_per_pixel = 2; video->bytes_per_line = VGA_WIDTH * 2; video->red_shift = 0; video->green_shift = 0; video->blue_shift = 0; video->red_size = 0; video->green_size = 0; video->blue_size = 0; } void init_graphics(boot_info_t *info) { if (!info) { return; } video_info_t *video = &info->video; edid_info_t *edid = &info->edid; kernel_args_t *args = &info->args; text_video(video); if (info->args.video == VIDEO_NONE) { return; } edid_data_t edid_data = { 0 }; if (!fetch_edid(&edid_data)) { edid_res((u8 *)&edid_data, &info->edid); video->width = min(args->vesa_width, edid->monitor_width); video->height = min(args->vesa_height, edid->monitor_height); log_debug("detected monitor %ux%u", (unsigned int)video->width, (unsigned int)video->height); } if (info->args.video == VIDEO_GRAPHICS) { vesa_mode_t vesa = pick_mode(video->width, video->height, info->args.vesa_bpp); if (vesa.bits_per_pixel) { video->mode = VIDEO_GRAPHICS; video->framebuffer = (u64)vesa.framebuffer; video->width = vesa.width; video->height = vesa.height; video->bytes_per_pixel = (u32)vesa.bits_per_pixel / 8; video->bytes_per_line = (u32)vesa.bytes_per_line; video->red_shift = vesa.red.position; video->green_shift = vesa.green.position; video->blue_shift = vesa.blue.position; video->red_size = vesa.red.mask; video->green_size = vesa.green.mask; video->blue_size = vesa.blue.mask; log_info("video graphics %ux%u", (unsigned int)video->width, (unsigned int)video->height); return; } } log_info("video VGA text"); }