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:
Nexoniarz 2026-10-22 23:46:58 +02:00
commit 60d06658f0
9 changed files with 347 additions and 8 deletions

View file

@ -167,6 +167,7 @@ cprintf(char *fmt, ...)
if (locking)
acquire(&cons.lock);
vcprintf(fmt, ap);
gfx_flush();
if (locking)
release(&cons.lock);
va_end(ap);
@ -200,6 +201,7 @@ kinfo(char *fmt, ...)
vcprintf(fmt, ap);
if (!boot_verbose)
cons_logonly--;
gfx_flush();
if (locking)
release(&cons.lock);
va_end(ap);
@ -645,6 +647,7 @@ consoleintr(int (*getc)(void))
}
}
release(&input_lock);
gfx_flush(); // the echo
}
int
@ -759,6 +762,7 @@ consolewrite(struct inode *ip, uint64 off, char *buf, int n)
}
for(i = 0; i < n; i++)
vt_out(v, buf[i] & 0xff);
gfx_flush();
release(&cons.lock);
// And onto a second display, if the kernel drives one.