display: a 64-bit framebuffer address, no mirroring onto itself
Three fixes for real machines that broke at any resolution but 800x600: - kmain() took the loader's framebuffer address as uint32, and vga_fb_init() re-published the scanout from that and the 16-bit VBE block. An aperture above 4 GB moved the console onto the wrong memory right after the vga: line. Both now use the loader's 64-bit display block, mapped with ioremap(). - The loader mirrored onto the console splitter's GOP, which forwards to the primary itself, and onto GOPs that share the primary's aperture. After a mode change that rewrote the screen over itself with another mode's stride. Handles without a device path and overlapping apertures are skipped, and a mode change re-matches the remaining mirrors. - A mode whose reported size, stride or aperture does not add up after SetMode is refused, and the loader returns to the previous one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
c9790df04c
commit
fe32833ade
5 changed files with 193 additions and 16 deletions
|
|
@ -1212,3 +1212,46 @@ fallback while claiming not to be.
|
|||
"A font is installed" and "a typeface was chosen" had been the same test
|
||||
(`cfont == 0`) and quietly stopped being. They are two flags now, and
|
||||
`gfx_font_ready()` answers the second one.
|
||||
|
||||
---
|
||||
|
||||
## 23. Every resolution but 800x600, on two real machines
|
||||
|
||||
Two reports in one afternoon. A desktop with an NVIDIA card booted fine at
|
||||
800x600 and at anything else showed the kernel log as dotted lines across the
|
||||
screen, and the loader's splash as four squashed copies of the logo side by
|
||||
side. A laptop stopped dead after the kernel's `vga:` line. Three separate
|
||||
defects were found under them; the third is a guard rather than a cure, and
|
||||
the desktop has not yet been retested.
|
||||
|
||||
**The framebuffer address was cut in half.** The loader hands the kernel all
|
||||
sixty-four bits of the aperture address in RBX, and `kmain()` took it as a
|
||||
`uint32`. Below 4 GB that is harmless. Above it -- where a laptop's GPU is
|
||||
entitled to put its aperture -- the low half is a plausible address pointing
|
||||
at nothing. `kmain()` itself reads the loader's display block and puts the
|
||||
early console on the right memory, but `vga_fb_init()` then re-published the
|
||||
scanout from the truncated argument and the BIOS-era VBE block (32-bit
|
||||
address, 16-bit sizes). The second publication won, and every line after
|
||||
`vga:` went somewhere else. Both are fixed: `kmain(uint64)`, and
|
||||
`vga_fb_init()` reads the same 64-bit block `kmain()` does and maps it with
|
||||
`ioremap()` instead of `P2V()`.
|
||||
|
||||
**The loader mirrored the screen onto itself.** It copies its picture to
|
||||
every other GOP it finds, for machines with two displays. But the firmware's
|
||||
console splitter is itself a GOP, on a virtual handle, and its `Blt` forwards
|
||||
to every real adapter -- the primary included. A laptop that reaches one panel
|
||||
through two GOPs is the same thing with a shared aperture. Once the resolution
|
||||
menu changed the primary's mode, the splitter still described the old one, so
|
||||
each mirror pass wrote the primary's own pixels back over themselves at an
|
||||
offset and with another mode's row length. Handles without a device path (the
|
||||
splitter) and GOPs whose aperture overlaps the primary's are no longer mirror
|
||||
targets, and a mode chosen in the menu re-matches the mirrors that remain.
|
||||
QEMU's OVMF has the splitter too: `schos: 2 display adapter(s)`, both at the
|
||||
same address.
|
||||
|
||||
**A mode the firmware cannot describe is refused.** After `SetMode`, the
|
||||
loader now checks that the mode it got is the size it asked for, that the row
|
||||
is at least as long as the width, and that the aperture holds that many rows.
|
||||
If not, it goes back to the previous mode and says so in the menu. This proves
|
||||
nothing about a mode that passes, but it stops a lying one from reaching the
|
||||
kernel.
|
||||
|
|
|
|||
Loading…
Reference in a new issue