GraphicsOutputProtocol.SetMode() doing nothing on real hw

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
DanB91
Posts: 8
Joined: Thu Dec 12, 2019 7:07 am

GraphicsOutputProtocol.SetMode() doing nothing on real hw

Post by DanB91 »

Hi All,

I am writing a kernel which is a UEFI application. I attempting to change the screen resolution via GraphicsOutputProtocol->SetMode(). This works perfectly fine in QEMU and VirtualBox but does not seem to work on my desktop. Here are some details:
  • - On the desktop, resolution is fixed at 1024x768 and is not changed when SetMode() is called.
    - Resolution correctly changes on QEMU/VBOX
    - SetMode() returns success on the desktop, but does not change the mode.
    - To see the current mode, I look at GraphicsOutputProtocol->Mode->Mode.
    - The current mode in QEMU/VBOX properly reflects what is passed into SetMode() whereas on the desktop it is fixed to the mode for 1024x768
    - The input for SetMode() is gotten directly from QueryMode()
    - QueryMode() (seemingly) works fine on the desktop.
    - My desktop is using an Nvidia GTX 1070
    - I am calling SetMode() before I call ExitBootServices()
Is there anything obvious I am missing here, or is this a known problem? Any help would be greatly appreciated.

Thanks!
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: GraphicsOutputProtocol.SetMode() doing nothing on real h

Post by bzt »

DanB91 wrote:SetMode() returns success on the desktop, but does not change the mode
If this is true, then you have an UEFI firmware bug (or at least an UEFI video driver bug). SetMode should not return success without changing the mode, unless the requested mode is the currently set mode.
DanB91 wrote:Is there anything obvious I am missing here
Nope, I think you're doing fine. You must query GOP->Mode->MaxMode with QueryMode() and then set one of them with SetMode(), and all that before calling ExitBootServices(). What you wrote seems okay to me, but we should take a look at your source code to be sure.
DanB91 wrote:is this a known problem?
Well, not this one, but there's a known problem with more drivers that QueryMode() returns garbage and GOP->Mode->Mode is invalid if SetMode() is not called. I believe I've wrote about this on the wiki.
DanB91 wrote:Any help would be greatly appreciated.
Take a look at my code, which is known to work. This GetLFB() function sets the mode reqwidth x reqheight x 32 bpp. It's been tested not only on VMs, but on many real hardware configurations too (by many users).

Cheers,
bzt
DanB91
Posts: 8
Joined: Thu Dec 12, 2019 7:07 am

Re: GraphicsOutputProtocol.SetMode() doing nothing on real h

Post by DanB91 »

bzt wrote:[Take a look at my code, which is known to work. This GetLFB() function sets the mode reqwidth x reqheight x 32 bpp. It's been tested not only on VMs, but on many real hardware configurations too (by many users).

Cheers,
bzt
Thanks a lot! I am looking at your code and unfortunately I don't see much difference between yours and my code, except for the fact that I am ignoring Pixels PerScanLine for now (I am only supporting 4 bytes per pixel modes). I have attached a gist of my code below and a screenshot of what it looks like on QEMU. This code essentially prints out a list of supported modes based on QueryMode and enumerates this list. The user then inputs the number of the mode and SetMode is called on that number. Please take note of line 127 which is where I call SetMode.


The code is written in Zig instead of C, but hopefully it shouldn't too hard to translate. For example instead of

Code: Select all

status = uefi_call_wrapper(gop->SetMode, 2, gop, 0);
it is

Code: Select all

var set_mode_status = gop.setMode(selected_mode);
.

Please let me know if you have any issues going through the code. Thanks!

GIST: https://gist.github.com/DanB91/25777f6f ... 1ddb0399d0
Screenshot: https://i.imgur.com/hs8sofT.png
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: GraphicsOutputProtocol.SetMode() doing nothing on real h

Post by bzt »

DanB91 wrote:I am ignoring Pixels PerScanLine for now (I am only supporting 4 bytes per pixel modes).
Bytes per pixel doesn't matter, you should always use the scanline when calculating screen offsets. I only support 4 bytes per pixel too, yet I've seen many different scanlines. For 1024 width it's always 4096, but for 800 it varies on every hardware. It is very typical that scanline is not 3200 as one would expect (4*800), but 4096, because there are "phatom" pixels at the end of each line to make every row aligned. However this only matters after you successfully set a mode and got a framebuffer address.
DanB91 wrote:I have attached a gist of my code below and a screenshot of what it looks like on QEMU. This code essentially prints out a list of supported modes based on QueryMode and enumerates this list. The user then inputs the number of the mode and SetMode is called on that number. Please take note of line 127 which is where I call SetMode.
After LocateProtocol and before the loop, you should have a QueryMode, and if that returns "not started" error, then call SetMode with mode argument of 0. Otherwise the values in the gop struct might be unreliable.
DanB91 wrote:The code is written in Zig instead of C, but hopefully it shouldn't too hard to translate.
I don't know. UEFI uses a special ABI, does Zig handle that correctly? I haven't use Zig, but in C the compiler's default ABI differs from the firmware's ABI, that's why there's a need for the uefi_call_wrapper(). You could configure the C compiler to use the UEFI ABI internally, but I'm not sure that's the case with the Zig compiler. Maybe someone more experienced with Zig can tell you that.

Cheers,
bzt
DanB91
Posts: 8
Joined: Thu Dec 12, 2019 7:07 am

Re: GraphicsOutputProtocol.SetMode() doing nothing on real h

Post by DanB91 »

I just figured it out! So it turns out it was due to a function call I actually omitted in my GIST because I thought it was unimportant. This function call happened after calling SetMode(), but before looking at GOP->Mode. This function call had these 3 lines:

Code: Select all

_ = std.os.uefi.system_table.con_out.?.reset(false);
_ = std.os.uefi.system_table.con_out.?.clearScreen();
_ = std.os.uefi.system_table.con_out.?.setCursorPosition(0, 0);
I updated my GIST and and put the 3 lines at line 169. I guess if you try to reset ConOut, it mucks with the mode? Regardless, I removed these lines and it now correctly changes resolution on my desktop! I guess when all else fails, double check the things you think are unimportant lol. Thanks a lot for the help.
Post Reply