gfx: draw the console in RAM, and write it to the card write-combined
On the first real machine with a discrete card (an RTX 3060, 800x600 from the UEFI GOP) the console was slow enough to photograph mid-scroll, and the boot died on CPU 1's "cpu1: starting": CPU 0 spun 200M times on the console lock while CPU 1 held it, scrolling, and called it a deadlock. The framebuffer is device memory in the uncached 3-4 GB page of the direct map. QEMU's stdvga is RAM underneath, so nothing showed there; on a card, gfx_scroll_up's memmove read the whole screen back across the bus one byte at a time, and every anti-aliased glyph read back each pixel it blended. So the console now draws into a copy of the screen in RAM (vmap_alloc: kalloc pages behind a page table of their own, since nothing needs it contiguous), and gfx_flush() copies the changed rectangle onto the card through an ioremap_wc() mapping -- PAT entry 4 set to write-combining on every CPU. Flushes happen where drawing ends: a message, a terminal write, an echo, a terminal switch. Scrolling moves eight bytes at a time instead of one. /dev/fb0 reads and writes the card itself, as before. The early console's fb_info now carries the physical address it always should have. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
93f981c7ea
commit
60d06658f0
9 changed files with 347 additions and 8 deletions
|
|
@ -12,6 +12,7 @@
|
|||
#include "klog.h"
|
||||
#include "usb.h"
|
||||
#include "acpi.h"
|
||||
#include "vmap.h"
|
||||
|
||||
static void *klog_base; // the persistent log, just above the kernel image
|
||||
|
||||
|
|
@ -137,7 +138,9 @@ kmain(uint32 fb_addr)
|
|||
bi.stride = *(uint16*)P2V(SCHBOOT_VBE_STRIDE);
|
||||
bi.bpp = *(uchar *)P2V(SCHBOOT_VBE_BPP);
|
||||
}
|
||||
bi.ptr = (volatile uchar *)P2V(fb_phys);
|
||||
bi.ptr = (volatile uchar *)P2V(fb_phys);
|
||||
bi.phys = fb_phys;
|
||||
bi.size = (uint64)bi.stride * bi.height;
|
||||
|
||||
// Only if the loader actually described something, and only if the direct
|
||||
// map reaches it. A framebuffer above 4 GB needs ioremap(), and ioremap()
|
||||
|
|
@ -201,6 +204,7 @@ kmain(uint32 fb_addr)
|
|||
dma_init(P2V(lowtop - DMA_ARENA_BYTES), P2V(lowtop));
|
||||
if (boot_verbose) cprintf("verbose boot: dma_init done\n");
|
||||
kvmalloc();
|
||||
pat_init();
|
||||
if (boot_verbose) cprintf("verbose boot: kvmalloc done\n");
|
||||
|
||||
// Now that the direct map reaches it, the rest of RAM can be allocated from.
|
||||
|
|
@ -231,6 +235,10 @@ kmain(uint32 fb_addr)
|
|||
(void *)fb_early);
|
||||
}
|
||||
}
|
||||
// The console has been drawing straight onto the card. From here it draws
|
||||
// into RAM and copies out. See gfx_flush().
|
||||
gfx_accelerate();
|
||||
|
||||
// ACPI first, the legacy MP table only if it finds nothing. Modern firmware
|
||||
// publishes a complete MADT and an MP table with one processor in it, so
|
||||
// asking the older interface first means never seeing more than one CPU.
|
||||
|
|
@ -409,6 +417,7 @@ mpenter(void)
|
|||
// AP keeps whatever mode the firmware left its APIC in across INIT, so the
|
||||
// boot processor having switched itself back says nothing about this one.
|
||||
lapic_xapic_mode();
|
||||
pat_init();
|
||||
switchkvm();
|
||||
seginit();
|
||||
lapicinit();
|
||||
|
|
|
|||
Loading…
Reference in a new issue