Page 1 of 2

EFI GOP lying about screen resolution?

Posted: Wed Mar 06, 2024 3:40 pm
by belliash
Hello,

I came across strange issue when trying to print text on one of my laptops.
Everything started when I started testing my code on bare metal, and one machine behaved different than others. Instead of text, it showed some trash on the screen.
I started investigating this and after a while I realized that the problem is not connected with my putchar() implementation, but with framebuffer itself.
I have not noticed anything earlier, as all I tested on bare metal was to clear screen (or draw full screen filled rectangle), what worked fine. But when I modified my clearscreen function to draw clear just the 1/4 of the screen it also produced some artifacts.

I use GOP and GOP reports that current mode is 1024x768x32. So I use below code to draw a horizontal line at the top of the screen:

Code: Select all

    for(PositionX = 0; PositionX < FrameBufferData.Width; PositionX++)
      FrameBufferData.Address[PositionX] = 0xFFFFFF;
and it is drawn too long (fills whole line 0 and a bit more than half of line 1). I tried to use hardcoded value (1024) instead of FrameBufferData.Width, but this gives exact result.

When I wanted to draw a rectangle (0,0, 1024/2, 768/2), what on qemu and other hardware looked fine, with below code:

Code: Select all

    for(PositionX = 0; PositionX < HlpFrameBufferData.Width / 2; PositionX++)
    {
        for(PositionY = 0; PositionY < HlpFrameBufferData.Height / 2; PositionY++)
        {
            FrameBufferIndex = PositionY * HlpFrameBufferData.Width + PositionX;
            HlpFrameBufferData.Address[FrameBufferIndex] = 0x00000077;
        }
    }
this one affected laptop gave me the following result:
Image

What is strange, when I try to fill full screen, it works properly. But that's the only thing that works here...

This is what GOP protocol reports about video mode:
Image

Finally, I tried running Limine 7.0.5. and the result is very similar:
Image

Anyone came across similar issue?
Is it possible to get FB working properly on this hardware or should I consider this as a firmware bug and don't bother about it any longer?

Re: EFI GOP lying about screen resolution?

Posted: Sat Mar 09, 2024 8:31 pm
by Octocontrabass
What laptop is that?

Do you see the same problem if you boot Windows on that laptop? If you do, it's a firmware bug. (The screen will return to normal when Windows loads a driver.)

Re: EFI GOP lying about screen resolution?

Posted: Sun Mar 10, 2024 2:03 am
by belliash
This is Lenovo S205. I know it is old and buggy.
However I have discovered several things:

1. My boot loader and Limine looks very similar, as on the attached screenshot.
2. GRUB does not wont to use GOP - it says that there is no valid video mode available (VESA on legacy boot works fine).
3. rEFInd displays images correctly, but it seems to use Gop->Blt()
4. Windows 10 and Windows 11 booted with EFI displays Windows boot screen correctly, and properly draws text on BSOD.

While points 1-3 suggest that this is broken firmware (and probably it is), point 4 shows that it is possible to work with it. I don't believe it detects broken firmware or implement some quirks and have no idea why it is using framebuffer correctly. For sure this is not related to drivers, as they are loaded later (after boot screen is displayed).

Re: EFI GOP lying about screen resolution?

Posted: Sun Mar 10, 2024 3:44 pm
by zaval
Please, try this

Code: Select all

void DrawRectangle(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop){
	UINT32	ppsl = gop->Mode->Info->PixelsPerScanLine;
	UINT32	*fbb = (UINT32 *)gop->Mode->FrameBufferBase;


	for(int i = 0; i < 0x10; i++){
		for(int j = 0; j < 0x80; j++){
			*(fbb + i * ppsl + j) = 0x00804000;
		}
	}
	return;
}
what it would show.

Re: EFI GOP lying about screen resolution?

Posted: Sun Mar 10, 2024 4:12 pm
by Octocontrabass
belliash wrote:Windows 10 and Windows 11 booted with EFI displays Windows boot screen correctly,
That's interesting. I'll have to see if they're doing anything special with GOP to fix the framebuffer.
belliash wrote:and properly draws text on BSOD.
I don't think the BSOD can use GOP, unless you don't have a display driver.

Re: EFI GOP lying about screen resolution?

Posted: Mon Mar 11, 2024 2:27 am
by rdos
A lot of UEFI manufacturers don't care about GOP, and just throw in a few standard resolutions they get from Intel. That's because Windows and Linux have their own drivers so do not rely on GOP. Typically, they will do the same with legacy boot.

In my experience, there is a higher chance to get correct resolutions from AMD based computers.

Re: EFI GOP lying about screen resolution?

Posted: Mon Mar 11, 2024 10:06 pm
by linuxyne
If you are motivated enough, you can try enabling the debug logs for the "video" tag when trying out grub.

See for instance, the function grub_video_gop_setup within efi_gop.c

Does the laptop have function-key combination that shows the current resolution as known by the display panel?

Re: EFI GOP lying about screen resolution?

Posted: Tue Mar 12, 2024 12:33 am
by belliash
Octocontrabass wrote:I don't think the BSOD can use GOP, unless you don't have a display driver.
It uses framebuffer for sure. However I cannot be sure if it is provided by GOP or any 3rd party driver.
This laptop has CSM enabled, and it provides EFI_LEGACY_BIOS_PROTOCOL. So it might be used in some way to get VESA modes as well. However I was unable to use the following code:

Code: Select all

    Regs.X.AX = 0x4F00;
    Regs.X.ES = (UINT_PTR)VbeInfo;
    Regs.X.DI = (UINT_PTR)VbeInfo;
    X = LegacyBios->Int86(LegacyBios, 0x10, &Regs);
Int86 returns FALSE all the time, and VbeInfo stays zeroed.
However I could successfully switch to 80x25 text mode with:

Code: Select all

   Regs.H.AH = 0x00;
   Regs.H.AL = 0x03;
 LegacyBios->Int86(LegacyBios, 0x10, &Regs);
   Regs.H.AH = 0x11;
   Regs.H.AL = 0x14;
   Regs.H.BL = 0;
 LegacyBios->Int86(LegacyBios, 0x10, &Regs);
rdos wrote:In my experience, there is a higher chance to get correct resolutions from AMD based computers.
This laptop is based on AMD E-350 APU.

linuxyne wrote:If you are motivated enough, you can try enabling the debug logs for the "video" tag when trying out grub.
GRUB does not work with GOP on this laptop. It says there are no valid modes available.


zaval wrote:Please, try this

Code: Select all

void DrawRectangle(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop){
	UINT32	ppsl = gop->Mode->Info->PixelsPerScanLine;
	UINT32	*fbb = (UINT32 *)gop->Mode->FrameBufferBase;


	for(int i = 0; i < 0x10; i++){
		for(int j = 0; j < 0x80; j++){
			*(fbb + i * ppsl + j) = 0x00804000;
		}
	}
	return;
}
what it would show.
On QEMU it displays a small bar (like progress bar) in top left corner. On Lenovo S205 it looks as follows:
Image

Re: EFI GOP lying about screen resolution?

Posted: Tue Mar 12, 2024 7:06 am
by linuxyne
belliash wrote:
linuxyne wrote:If you are motivated enough, you can try enabling the debug logs for the "video" tag when trying out grub.
GRUB does not work with GOP on this laptop. It says there are no valid modes available.
The intention was to have grub expose the reason it isn't able to find valid modes. Nevermind, though.

From the various outputs pasted on the thread, it looks like the framebuffer isn't actually linear but tiled. The GPU may implement various types of tiling and/or compression for the various buffers that it uses including scanout, textures, etc. This may be a reason Gop->Blt and ConsoleOut work, but directly addressing the buffer does not.

Edit:
Does UEFI have the separate modes of working with console vs working with framebuffer?

Is it possible that the firmware isn't expecting anybody to draw on the framebuffer in console mode, and so the framebuffer it exposes may not be linear?

I was going to suggest drawing your graphics after executing a SetMode; SetMode may cause the firmware to setup a frame-buffer in a non-tiled linear mode, but it still needs to be seen if you are able to query the modes in the first place (given that grub for some reason says no valid video mode available).

Re: EFI GOP lying about screen resolution?

Posted: Tue Mar 12, 2024 3:32 pm
by zaval
Aha, I see, yes, that p1tch lies to you. Judging by the screwing pattern, its horizontal resolution is 640 and PixelsPerScanline is 640 as well. You can check it is so by running (on the affected machine only) the modified function:

Code: Select all

void DrawRectangle(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop){
	//UINT32	ppsl = gop->Mode->Info->PixelsPerScanLine;
	UINT32	*fbb = (UINT32 *)gop->Mode->FrameBufferBase;


	for(int i = 0; i < 0x10; i++){
		for(int j = 0; j < 0x80; j++){
			*(fbb + i * 0x280 + j) = 0x00004080;
		}
	}
	return;
}
I bet, it will fix it. Another buggy firmare implementation with no sane workaround. except, maybe, you can try to request it to set to one of the supported modes. Maybe this is how Windows loader makes its magic of getting this cr4p to work correctly.

Re: EFI GOP lying about screen resolution?

Posted: Wed Mar 13, 2024 12:33 am
by belliash
linuxyne wrote:
belliash wrote:
linuxyne wrote:If you are motivated enough, you can try enabling the debug logs for the "video" tag when trying out grub.
GRUB does not work with GOP on this laptop. It says there are no valid modes available.
The intention was to have grub expose the reason it isn't able to find valid modes. Nevermind, though.

From the various outputs pasted on the thread, it looks like the framebuffer isn't actually linear but tiled. The GPU may implement various types of tiling and/or compression for the various buffers that it uses including scanout, textures, etc. This may be a reason Gop->Blt and ConsoleOut work, but directly addressing the buffer does not.

Edit:
Does UEFI have the separate modes of working with console vs working with framebuffer?

Is it possible that the firmware isn't expecting anybody to draw on the framebuffer in console mode, and so the framebuffer it exposes may not be linear?

I was going to suggest drawing your graphics after executing a SetMode; SetMode may cause the firmware to setup a frame-buffer in a non-tiled linear mode, but it still needs to be seen if you are able to query the modes in the first place (given that grub for some reason says no valid video mode available).
I thought it is same mode, as on all hardware I got, I was able to draw on framebuffer as well as use EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
I tried changing mode with the following code:

Code: Select all

DefaultMode = Gop->Mode->Mode; // by default this is set to 9
Gop->SetMode(Gop, 1);
Gop->SetMode(Gop, DefaultMode);
Graphics mode changes, screen gets cleared and I can still use EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL, however the font looks different, even I set same mode as before (9 -> 1 -> 9). However I need to mention that I force 80x25. So after changing GOP screen mode it can reset to something else, like 130x25.

Also the brown bar in top left corner displays correctly after this, so apparently looks like you are right. Switching mode (even setting same mode) switches framebuffer to linear (or something else happens).
What I also noticed is that EfiST->ConOut->ClearScreen(EfiST->ConOut); does not clear that "progress bar" draw on the screen after changing mode. And this happens not only on affected Lenovo S205. I tested this on another hardware as well and while, there was no other difference, it as well was unable to remove that bar with ConOut->ClearScreen(). This worked only under OVMF so far.

Also as you can see on screenshot below, FB size is 0 bytes:
Image

zaval wrote:Aha, I see, yes, that p1tch lies to you. Judging by the screwing pattern, its horizontal resolution is 640 and PixelsPerScanline is 640 as well. You can check it is so by running (on the affected machine only) the modified function:

Code: Select all

void DrawRectangle(EFI_GRAPHICS_OUTPUT_PROTOCOL *gop){
	//UINT32	ppsl = gop->Mode->Info->PixelsPerScanLine;
	UINT32	*fbb = (UINT32 *)gop->Mode->FrameBufferBase;


	for(int i = 0; i < 0x10; i++){
		for(int j = 0; j < 0x80; j++){
			*(fbb + i * 0x280 + j) = 0x00004080;
		}
	}
	return;
}
I bet, it will fix it. Another buggy firmare implementation with no sane workaround. except, maybe, you can try to request it to set to one of the supported modes. Maybe this is how Windows loader makes its magic of getting this cr4p to work correctly.
This works as well, but this way, we cannot be sure what screen resolution it is.
Image

Re: EFI GOP lying about screen resolution?

Posted: Wed Mar 13, 2024 1:58 am
by linuxyne
belliash wrote: I thought it is same mode, as on all hardware I got, I was able to draw on framebuffer as well as use EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
That behaviour may not be mandated by the UEFI spec, especially in the Console (Text) mode. For instance, if the firmware is allowed to rely on the VGA text-mode for its console (text) mode, there wouldn't be a frame-buffer-like, pixel-addressable interface available. Digging through the spec, or having someone more knowledgable about UEFI respond, could help.

A search shows two enum constants related to (perhaps a non-standard protocol) EFI_CONSOLE_CONTROL_PROTOCOL (CCP):

Code: Select all

EfiConsoleControlScreenGraphics
EfiConsoleControlScreenText
rEFInd seems to make use of CCP when switching the screen between console and graphics.
belliash wrote: What I also noticed is that EfiST->ConOut->ClearScreen(EfiST->ConOut); does not clear that "progress bar" draw on the screen after changing mode. And this happens not only on affected Lenovo S205. I tested this on another hardware as well and while, there was no other difference, it as well was unable to remove that bar with ConOut->ClearScreen(). This worked only under OVMF so far.
The firmware could possibly allocate different buffers for Console/UGA/GOP modes, and overlay them based on how these modes are being utilized by the UEFI client programs (for instance, to allow quicker switching between the modes). If that is the case, clearing one buffer may or may not affect the other buffers.
belliash wrote: Also as you can see on screenshot below, FB size is 0 bytes:
Can't say why it is zero.

----

I think maintaining a clear separation between the 3 modes Console/UGA/GOP, and utilizing them in a mutually exclusive manner, based on which of them are desired and available, may help sidestep these problems. Looking up the spec, other boot loaders/managers (licenses permitting) as rEFInd, systemd-boot, etc., or contacting the support forums of the UEFI vendors (Lenovo here, or even AMD), could help in determining how the client programs are expected to behave.

Do UEFI firmwares get updates the way BIOSes get them? If so, and if the firmware isn't the latest for the laptop model you are running, updating it may bring its features to the same parity/level as those we expect from it.

It is likely that at least some of the behaviours, if not all, being demonstrated here cannot be fully attributed to being a result of something broken in the firmware.

---

Edit: Despite that fact that drawing on the framebuffer returned by GOP seems to overwrites the ConsoleOut text on the screen, that doesn't imply that the framebuffer backing the text-output protocol and that backing the GOP are the same. It may be the case that they are separate, and are being overlayed by the GPU scanout hardware, giving the illusion that one is drawing on the framebuffer associated with the text-out protocol. If the buffers aren't the same, then, it is likely that a SetMode is necessary on GOP to inform the firmware that a client-renderable linear buffer must be configured for GOP.

In case the framebuffers are exactly the same (as I have been assuming thus far), the fact that ConsoleOut is able to properly display your bootloader's menu must imply that, internally, the GPU knows how to arrange font bitmaps on the very buffer that, if we try to draw to directly, shows tiling-like artifacts. This is a very good indicator that, if the buffers are the same, then tiling is in effect.

Also, ipxe boot-loader has a comment that says that CCP was relevant as an older, EDK-based, non-standard protocol to control the text/graphics sub-modes for the console, implemented by some older version 1.10 UEFI firmware.

Again, all of this is based on the symptoms here, and need to be corroborated with the spec, or with someone who knows the spec.

---

Edit2:

I have also been assuming that the resolution returned by the GOP matches the panel's resolution, further also assuming that there is no hardware on this laptop like there is Hardware Video Scaler on Raspberry Pis that can carry out scanline resizing on the fly, i.e. system-set resolution will be, for instance, 1024x768 but the panel is running at 1920x1080, and the scanlines from the 1024x768 framebuffer are enlarged using image processing filters.

If the resolution returned by GOP isn't the resolution at which the screen is running (hence my initial question about Fn key combination), then SetMode seems to be a way out of this situation. Querying the GOP's current resolution without first running SetMode on it could perhaps be returning something that's not applicable to the current ConsoleOut screen.

Re: EFI GOP lying about screen resolution?

Posted: Wed Mar 13, 2024 5:30 am
by belliash
linuxyne wrote:Querying the GOP's current resolution without first running SetMode on it could perhaps be returning something that's not applicable to the current ConsoleOut screen.
Shouldn't it return EFI_NOT_STARTED then?
linuxyne wrote:Again, all of this is based on the symptoms here, and need to be corroborated with the spec, or with someone who knows the spec.
This is old hardware (over 10 years). I suppose vendor will not be interested in investigating this or even trying t o explain how to proceed.
linuxyne wrote:I have also been assuming that the resolution returned by the GOP matches the panel's resolution, further also assuming that there is no hardware on this laptop like there is Hardware Video Scaler on Raspberry Pis that can carry out scanline resizing on the fly, i.e. system-set resolution will be, for instance, 1024x768 but the panel is running at 1920x1080, and the scanlines from the 1024x768 framebuffer are enlarged using image processing filters.
I think that's also possible, especially it says it is 1024x768 while panel is 1366x768 and whole screen is filled and there is no resize effect.
linuxyne wrote:Can't say why it is zero.
That was actually bug in my code ;)

Re: EFI GOP lying about screen resolution?

Posted: Wed Mar 13, 2024 9:55 am
by linuxyne
belliash wrote: Shouldn't it return EFI_NOT_STARTED then?
I guess so; rEFInd also does call Gop.SetMode if there isn't any need for video-mode-change.
belliash wrote: This is old hardware (over 10 years). I suppose vendor will not be interested in investigating this or even trying t o explain how to proceed.
I think so, but the support forums may have people who have seen this before, and may know the reason the behaviour occurs.
belliash wrote:
linuxyne wrote:Can't say why it is zero.
That was actually bug in my code ;)
That's a relief. Else this would have been yet another problem on top of everything else here.

---

IIUC, we want to be able to write text using text-based ConOut device available through SimpleTextOutputProtocol, while simultaneously drawing graphics on the screen using GOP.
It also seems that we expect such a mixture of ConOut and GOP to work. But at least one loader, rEFInd, maintains a clear separation of ConOut (TextMode in rEFInd terms) vs GOP (GraphicsMode in rEFInd terms). Moreover, to my UEFI-ignorant mind, supporting such a mixture seems to be unnecessary, and impossible in a case where the ConOut depends on VGA's text-mode that does not expose a pixel-addressable buffer.

Does the UEFI spec say that the framebuffer (if any) supporting the text-based SimpleTextProtocol, and the framebuffer supporting the GOP, are same/compatible?

Given that GOP and ConOut are different protocols, any sharing of framebuffers between them has to be inadvertent, at least at the protocol level if not at the implementation level, no?

Since the ConOut is 80x25 chars, with each char of 8x16 pixels, its framebuffer's resolution must be 640x400. And since the strings written out using ConOut are displayed on the screen properly, the screen resolution too must be 640x400. (Regardless of the presence/absence of HVS type hardware. The effect of such hw is transparent, as long as as we do not poke into the state of the panel.)

If GOP reports that *its* framebuffer's resolution is 1024x768, then isn't it more likely that the GPU has been setup with a single frame buffer of 1024x768, shared between the ConOut and the GOP, with ConOut utilizing only a portion, and with the screen resolution set to 640x400?

The APU seems to have a Radeon GPU of the Evergreen series. Poking at its registers can give some idea of how it has been setup, though there's no need to go this far.

If ConOut is available even after Gop.SetMode, then it is better to adjust ConOut (as you mentioned earlier) according to the Gop's resolution, instead of the other way around (trying to force GOP into ConOut's resolution) that we are trying to achieve.

Re: EFI GOP lying about screen resolution?

Posted: Wed Mar 13, 2024 7:11 pm
by zaval
belliash wrote: This works as well, but this way, we cannot be sure what screen resolution it is.
Image
I didn't suggest using this as an overcome, just for ensuring the firmware exposes a 640 pixels wide framebuffer, despite reporting it as 1024 pixels wide.

It's an implementation's bug, there is not much to do to fix it on your side. As an overcome, I suggested that you searched through available resolutions, it reports, picked the best for you and asked it to set. Maybe that way, this inconsistency will be gone. Apparently, it's an unnecessary hack for normal firmwares, it's up to you to decide to either insert it or not. I wouldn't, for example, because there are many other bugs and all of them would require addition of similar hacks, turning the loader into a bloated piece like grub. I added one such hack and decided "no more such ugliness". :D

To linuxine. You overcomplicated all heavily. There is no "text" mode in UEFI. The spec doesn't require from loaders to worry about STOP and GOP coexistence. If there are no bugs, they coexist normally. The framebiffer is always a linear array of 4 byte pixels. This case is just a bug. Unfortunately, they are very common.