feat: graphical framebuffer support for VGA driver

When GRUB is configured with multiboot2, it may provide a graphical
(RGB) framebuffer instead of legacy VGA text mode. This happens on
UTM/QEMU on macOS and other configurations where the bootloader
switches to a pixel-based display.

- Parse multiboot2 framebuffer tag in kernel_main to detect display mode
- Identity-map the framebuffer address (often at 0xFD000000+) after
  paging is enabled, with write-through caching
- Add framebuffer.h describing the boot-time display info
- Embed 8x16 VGA bitmap font (font8x16.h) for pixel-mode rendering
- Rewrite VGA driver to support both text mode and pixel mode:
  - Text mode: unchanged behavior writing to 0xB8000
  - Pixel mode: renders characters using the bitmap font to the
    GRUB-provided framebuffer, with proper VGA color palette mapping
  - Auto-detects mode from fb_info at init time
- Multiboot2 header now requests text mode via framebuffer tag, but
  gracefully falls back to pixel rendering if GRUB provides RGB
- Reverted grub.cfg gfxpayload=text (caused display issues on UTM)

Tested: boots in both text mode and graphical framebuffer mode.
This commit is contained in:
AI
2026-02-23 13:25:42 +00:00
parent 000d53e2f3
commit 993cf05712
5 changed files with 624 additions and 83 deletions

View File

@@ -1,6 +1,7 @@
#include <multiboot2.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "gdt.h"
#include "idt.h"
#include "pic.h"
@@ -17,6 +18,10 @@
#include "vfs.h"
#include "initrd_fs.h"
#include "keyboard.h"
#include "framebuffer.h"
/* Global framebuffer info, parsed from multiboot2 tags. */
framebuffer_info_t fb_info;
void offset_print(const char *str)
{
@@ -60,8 +65,9 @@ void kernel_main(uint32_t magic, uint32_t addr) {
init_pmm(addr);
offset_print("PMM initialized\n");
/* Scan Multiboot2 tags for the initrd module */
/* Scan Multiboot2 tags for the initrd module and framebuffer info */
uint32_t initrd_start = 0, initrd_end = 0;
memset(&fb_info, 0, sizeof(fb_info));
{
struct multiboot_tag *tag;
for (tag = (struct multiboot_tag *)(addr + 8);
@@ -75,7 +81,36 @@ void kernel_main(uint32_t magic, uint32_t addr) {
print_hex(initrd_start);
offset_print(" to ");
print_hex(initrd_end);
break; /* Use first module */
}
if (tag->type == MULTIBOOT_TAG_TYPE_FRAMEBUFFER) {
struct multiboot_tag_framebuffer *fbt =
(struct multiboot_tag_framebuffer *)tag;
fb_info.addr = (uint32_t)fbt->common.framebuffer_addr;
fb_info.pitch = fbt->common.framebuffer_pitch;
fb_info.width = fbt->common.framebuffer_width;
fb_info.height = fbt->common.framebuffer_height;
fb_info.bpp = fbt->common.framebuffer_bpp;
fb_info.type = fbt->common.framebuffer_type;
if (fb_info.type == FB_TYPE_RGB) {
fb_info.red_pos = fbt->framebuffer_red_field_position;
fb_info.red_size = fbt->framebuffer_red_mask_size;
fb_info.green_pos = fbt->framebuffer_green_field_position;
fb_info.green_size = fbt->framebuffer_green_mask_size;
fb_info.blue_pos = fbt->framebuffer_blue_field_position;
fb_info.blue_size = fbt->framebuffer_blue_mask_size;
}
offset_print("Framebuffer: type=");
print_hex(fb_info.type);
offset_print(" addr=");
print_hex(fb_info.addr);
offset_print(" ");
print_hex(fb_info.width);
offset_print(" x ");
print_hex(fb_info.height);
offset_print(" bpp=");
print_hex(fb_info.bpp);
}
}
}
@@ -83,6 +118,21 @@ void kernel_main(uint32_t magic, uint32_t addr) {
init_paging();
offset_print("Paging initialized\n");
/* If GRUB provided a graphical framebuffer, identity-map it so
* the VGA driver can write pixels to it after paging is enabled. */
if (fb_info.addr != 0 && fb_info.type == FB_TYPE_RGB) {
uint32_t fb_size = fb_info.pitch * fb_info.height;
uint32_t fb_base = fb_info.addr & ~0xFFFu; /* page-align down */
uint32_t fb_end = (fb_info.addr + fb_size + 0xFFF) & ~0xFFFu;
offset_print(" Mapping framebuffer ");
print_hex(fb_base);
offset_print(" to ");
print_hex(fb_end);
for (uint32_t pa = fb_base; pa < fb_end; pa += 4096) {
paging_map_page(pa, pa, PAGE_PRESENT | PAGE_WRITE | PAGE_WRITETHROUGH);
}
}
/* Test paging: allocate a page and write to it */
void *test_page = paging_alloc_page();
if (test_page) {