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:
54
src/kernel.c
54
src/kernel.c
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user