Control: tag -1 - patch

Hi,

Thanks for the proposed patch but as discussed elsewhere it seemed too
risky to force 32 bpp on everyone, so I went for what looked like the
least risky (adding bochs.ko and cirrus.ko, manually and for the time
being).

Ben Hutchings <b...@decadent.org.uk> (2023-05-14):
> I think the problem is this GRUB has native drivers for Bochs and
> Cirrus that reprogram the framebuffer bit depth, and the kernel is then
> confused about what the bit depth is supposed to be.  With QXL, GRUB
> doesn't have a native driver so it doesn't reconfigure the framebuffer.

I've spent some time trying to reproduce these issues under UEFI but
without Secure Boot, and I failed. So I've moved to learning how to sign
a Linux kernel (certutil, pesign, mokutil, etc.), and I've added some
debugging information in various places.

Under Secure Boot, with the default QEMU driver (std aka. bochs),
initialization happens via:

    drivers/firmware/efi/libstub/x86-stub.c

and its setup_graphics() that grabs the screen info part of boot params
and starts by zero-ing it:

    si = &boot_params->screen_info;
    memset(si, 0, sizeof(*si));

before trying efi_setup_gop() and setup_uga() in turn; the former being
current, the latter being the old standard.

Moving on to:

    drivers/firmware/efi/libstub/gop.c

we see that its efi_setup_gop() calls setup_gop(), which in turn calls
find_gop(). That last one gets hold of a suitable GOP pointer:
  
https://uefi.org/specs/UEFI/2.10/12_Protocols_Console_Support.html#graphics-output-protocol

The rest of setup_gop() then uses information contained within that
structure to derive all relevant information, filling the screen_info
structure. That structure is then trusted by efifb, which can do nothing
else but fail miserably…

The si (screen_info) is set starting here:
  
https://elixir.bootlin.com/linux/v6.1.27/source/drivers/firmware/efi/libstub/gop.c#L534

Adding some debug, here's what I get with GRUB set to 800x600x24:

    info->version: 0
    info->horizontal_resolution: 1024
    info->vertical_resolution: 768
    info->pixel_format: 1
      info->pixel_information.red_mask: 0
      info->pixel_information.green_mask: 0
      info->pixel_information.blue_mask: 0
      info->pixel_information.reserved_mask: 0
    info->pixels_per_scan_line: 1024

Let's see:

 - Of course width, height, and pixels_per_scan_line are incorrect.

 - pixel_format 1 means PIXEL_BGR_RESERVED_8BIT_PER_COLOR aka
   PixelBlueGreenRedReserved8BitPerColor in the spec, which means:

       A pixel is 32-bits and byte zero represents blue, byte one
       represents green, byte two represents red, and byte three is
       reserved. This is the definition for the physical frame buffer.
       The byte values for the red, green, and blue components represent
       the color intensity. This color intensity value range from a
       minimum intensity of 0 to maximum intensity of 255.

 - And masks are all 0.

So for this particular GRUB configuration to work, I've verified that
fixing all those fields was leading to a correct display via efifb
(having dropped bochs.ko to stick to efifb):

    info->horizontal_resolution = 800;
    info->vertical_resolution   = 600;
    info->pixels_per_scan_line  = 800;

    info->pixel_format = PIXEL_BIT_MASK;
    info->pixel_information.red_mask      = 0x00ff0000;
    info->pixel_information.green_mask    = 0x0000ff00;
    info->pixel_information.blue_mask     = 0x000000ff;
    info->pixel_information.reserved_mask = 0x00000000;

Setting PIXEL_BIT_MASK means masks become relevant, and bits set in
those are added to determine the actual color depth, instead of an
hardcoded 32, giving me (and efifb) 24. And even:

    efifb: mode is 800x600x24, linelength=2400, pages=1
    efifb: scrolling: redraw
    efifb: Truecolor: size=0:8:8:8, shift=0:16:8:0

instead of the dreaded:

    efifb: mode is 1024x768x32, linelength=4096, pages=1
    efifb: scrolling: redraw
    efifb: Truecolor: size=8:8:8:8, shift=24:16:8:0

Now, where to go from here? It seems pretty clear to me at this point
that the Linux kernel only relies on information that was obtained via
that GOP pointer, and does its best afterward.

The way the function call works seems pretty similar to what happens in
GRUB, so I'd think that the problem is likely *not* in the kernel, but
rather:

 - GRUB fails to set mode information properly.

 - OVMF drops the ball and return some default information.


> Unfortunately, with Secure Boot we have to use a monolithic GRUB build
> so I can't easily exclude video_bochs and video_cirrus to see if that
> improves matters.

Applying my new pesign skills on GRUB is the next step, but I have to
spend some time on another topic before Bookworm… It it possible that
trying to build a debug-enabled OVMF package might yield interesting
results, since AFAIUI that's the one implementing the back and forth…
If that's indeed the case, it should be easy to see what's written by
GRUB vs. what's read by Linux?


Cheers,
-- 
Cyril Brulebois (k...@debian.org)            <https://debamax.com/>
D-I release manager -- Release team member -- Freelance Consultant

Attachment: signature.asc
Description: PGP signature

Reply via email to