mirror of
https://github.com/subhobhai943/SUB-OS.git
synced 2026-10-25 16:52:29 +00:00
Pressing Esc left the desktop and started the shell, but nothing appeared in the
emulator window -- the session was only visible on the host terminal. Once the
VBE adapter is switched into graphics mode the VGA text buffer at 0xB8000 stops
being scanned out, so every printk after that point reached serial and the
dmesg ring but never the display.
Disabling the DISPI interface does not bring the text console back; QEMU keeps
presenting the last framebuffer surface. So rather than clawing the adapter back
to text mode, the console is now rendered into the framebuffer:
drivers/video/fbcon.c draws the console with the shared 8x8 font and carries a
VT100 subset -- SGR colours, cursor addressing, relative moves, erase-display,
erase-line and save/restore. It presents a fixed 80x25 grid, because that is
the geometry the userland assumes (nano pads its bars with %-80s, the shell
wraps at TTY_WIDTH), with the glyph cell scaled to fill the active resolution.
The TTY driver became the console dispatcher: tty_write, tty_clear and
tty_set_cursor route to fbcon while it owns the display. That last one is what
makes nano work -- it positions with tty_set_cursor() rather than escapes, so
its full-screen repaint was landing as an append-only stream.
Also in this change:
- `video=WIDTHxHEIGHT` on the kernel command line selects the display mode,
defaulting to 1280x720 so a 16:9 host panel is filled rather than pillarboxed
when QEMU goes fullscreen. Rejects modes the heap cannot back.
- `make run-fullscreen`, plus zoom-to-fit on the windowed target, so scaling
preserves the guest aspect ratio instead of stretching it.
- QEMU targets raise vgamem_mb, since a 1280x720 32-bit surface exceeds the
default VGA memory.
- bochs_vbe_disable()/fb_set_active() let the framebuffer be marked dormant so
drawing routines no-op after a mode teardown.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01L5yKJT4Wo2cV3rfPrwY5MP
130 lines
4.0 KiB
C
130 lines
4.0 KiB
C
// Bochs / QEMU VBE Display Adapter Driver for SUB-OS
|
|
#include <drivers/bochs.h>
|
|
#include <init/init.h>
|
|
#include <arch/x86_64/io.h>
|
|
#include <drivers/pci.h>
|
|
#include <drivers/fb.h>
|
|
#include <lib/string.h>
|
|
#include <kernel/printk.h>
|
|
|
|
static bochs_vbe_info_t vbe_info;
|
|
|
|
static bool g_vbe_enabled = false;
|
|
|
|
static void vbe_write(uint16_t index, uint16_t data) {
|
|
outw(VBE_DISPI_IOPORT_INDEX, index);
|
|
outw(VBE_DISPI_IOPORT_DATA, data);
|
|
}
|
|
|
|
static uint16_t vbe_read(uint16_t index) {
|
|
outw(VBE_DISPI_IOPORT_INDEX, index);
|
|
return inw(VBE_DISPI_IOPORT_DATA);
|
|
}
|
|
|
|
// Parse `video=WIDTHxHEIGHT` from the kernel command line. Falls back to the
|
|
// caller's request when the parameter is absent or malformed.
|
|
static void vbe_mode_from_cmdline(uint32_t* width, uint32_t* height) {
|
|
const char* spec = init_get_param("video");
|
|
if (!spec) return;
|
|
|
|
uint32_t w = 0, h = 0;
|
|
const char* p = spec;
|
|
|
|
while (*p >= '0' && *p <= '9') w = w * 10 + (uint32_t)(*p++ - '0');
|
|
if (*p != 'x' && *p != 'X') return;
|
|
p++;
|
|
while (*p >= '0' && *p <= '9') h = h * 10 + (uint32_t)(*p++ - '0');
|
|
|
|
// The Bochs adapter tops out at 2560x1600, and the compositor allocates a
|
|
// 32-bit backbuffer per surface, so refuse anything the heap cannot hold.
|
|
if (w < 640 || h < 480 || w > 1920 || h > 1200) {
|
|
printk(KERN_WARNING "VBE: ignoring unsupported video=%s\n", spec);
|
|
return;
|
|
}
|
|
|
|
*width = w;
|
|
*height = h;
|
|
}
|
|
|
|
void bochs_vbe_disable(void) {
|
|
if (!vbe_info.available) return;
|
|
|
|
vbe_write(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
|
|
g_vbe_enabled = false;
|
|
fb_set_active(false);
|
|
}
|
|
|
|
bool bochs_vbe_is_enabled(void) {
|
|
return g_vbe_enabled;
|
|
}
|
|
|
|
int bochs_vbe_set_mode(uint32_t width, uint32_t height, uint32_t bpp) {
|
|
if (!vbe_info.available) return -1;
|
|
|
|
vbe_write(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
|
|
vbe_write(VBE_DISPI_INDEX_XRES, (uint16_t)width);
|
|
vbe_write(VBE_DISPI_INDEX_YRES, (uint16_t)height);
|
|
vbe_write(VBE_DISPI_INDEX_BPP, (uint16_t)bpp);
|
|
vbe_write(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
|
|
|
|
vbe_info.current_width = width;
|
|
vbe_info.current_height = height;
|
|
vbe_info.current_bpp = bpp;
|
|
g_vbe_enabled = true;
|
|
|
|
// Connect global Framebuffer driver directly to hardware video memory
|
|
fb_set_hardware_lfb((uint32_t*)vbe_info.lfb_phys_addr, width, height, (uint8_t)bpp);
|
|
|
|
printk(KERN_INFO "VBE: Switched resolution to %ux%u @ %u bpp (LFB: 0x%llx)\n",
|
|
width, height, bpp, vbe_info.lfb_phys_addr);
|
|
return 0;
|
|
}
|
|
|
|
bool bochs_vbe_init(void) {
|
|
memset(&vbe_info, 0, sizeof(vbe_info));
|
|
|
|
uint32_t mode_w = 800, mode_h = 600;
|
|
|
|
// Probe VBE DISPI interface
|
|
uint16_t id = vbe_read(VBE_DISPI_INDEX_ID);
|
|
if ((id & 0xB0C0) == 0xB0C0) {
|
|
vbe_info.available = true;
|
|
vbe_info.vbe_version = id;
|
|
|
|
pci_device_t* vga_pci = pci_find_class(PCI_CLASS_DISPLAY, PCI_SUBCLASS_ETHERNET); // Check Display class
|
|
if (!vga_pci) vga_pci = pci_find_device(0x1234, 0x1111); // QEMU Standard VGA
|
|
if (!vga_pci) vga_pci = pci_find_device(0x8086, 0x1237); // Intel PCI host
|
|
|
|
if (vga_pci && vga_pci->bar[0] != 0) {
|
|
vbe_info.lfb_phys_addr = vga_pci->bar[0] & ~0xF;
|
|
} else {
|
|
vbe_info.lfb_phys_addr = 0xFD000000;
|
|
}
|
|
|
|
vbe_mode_from_cmdline(&mode_w, &mode_h);
|
|
bochs_vbe_set_mode(mode_w, mode_h, 32);
|
|
|
|
printk(KERN_INFO "VBE: Bochs/QEMU VBE Display Adapter detected (v%04x, Linear Framebuffer: 0x%llx)\n",
|
|
vbe_info.vbe_version, vbe_info.lfb_phys_addr);
|
|
return true;
|
|
}
|
|
|
|
// Software emulated VBE fallback
|
|
vbe_info.available = true;
|
|
vbe_info.vbe_version = 0xB0C5;
|
|
vbe_info.lfb_phys_addr = 0xFD000000;
|
|
vbe_mode_from_cmdline(&mode_w, &mode_h);
|
|
bochs_vbe_set_mode(mode_w, mode_h, 32);
|
|
|
|
printk(KERN_INFO "VBE: Standard VESA/VBE Display Engine online (800x600 32bpp TrueColor)\n");
|
|
return true;
|
|
}
|
|
|
|
bool bochs_vbe_is_available(void) {
|
|
return vbe_info.available;
|
|
}
|
|
|
|
const bochs_vbe_info_t* bochs_vbe_get_info(void) {
|
|
return &vbe_info;
|
|
}
|