Search code examples
assemblygraphics64-bituefi

Assembly - How to set graphics mode in UEFI (No VGA, No BIOS, Nothing deprecated)


Tools I'm using: nasm, qemu-system-x86_64.

Operating System I'm on: Windows 10.

So I checked out the "Real Mode OS Warning" at https://wiki.osdev.org/Real_Mode_OS_Warning

What the article seems to imply is that everything can be done without using BIOS interrupts whatsoever. I know how to load Long Mode, and so I've done this, but now I am stuck because BIOS interrupts were all I knew until now. I want to do something like set the graphics mode to full-memory-access-mode (might sound familiar if you've seen int 10h / AX = 4F02h / BX = 81FFh), but since I don't want to use something that's deprecated (BIOS), I have been having trouble searching the web for how to set the graphics mode and then access individual pixels in Long Mode only.

Hopefully it turns out to be possible to answer this question on StackOverflow. I have much faith that "it's too complicated" won't show up as an answer, especially since I was just told by OSDev NOT to use deprecated things. Telling someone it's too difficult assumes what they know and what they're able to learn without even knowing who they are. I just need a starting point to find out how to do this.

To give clarification, things that didn't work for me:

Enter graphics mode without interrupts in assembly

This didn't work for me because the answer gives a link to VGA, which I don't want.

Graphics mode in assembly 8086

This didn't work for me because the question does not ask about Long Mode, but rather about VGA graphics in Real Mode.

How to write data to a graphics card without using BIOS?

This didn't work for me because the answer was essentially "it's too complicated, use the deprecated stuff", which is the opposite of what i'm trying to do and quite contradictory to what I was just told on OSDev.

Drawing directly by graphics card on Intel 8086

This didn't work for me because the answer has nothing to do with setting the graphics mode.

A few x86 Assembly language questions

This didn't work for me because the answers do not say how to set the graphics mode in UEFI. They only talk about deprecated things.


Solution

  • Here is sample code that uses UEFI to get and print the available graphics modes on the first graphics device and optionally sets the mode.

    I used this reference: http://wiki.phoenix.com/wiki/index.php/EFI_GRAPHICS_OUTPUT_PROTOCOL.

    Notes on the implementation:

    • It calls LocateProtocol to obtain a Graphics Output Protocol. I tried using LocateHandle to obtain all the handles that support Graphics Output Protocol. It returned two handles, but OpenProtocol failed. I haven't had a chance to debug the version with LocateHandle. This version using LocateProtocol works.
    • It prints the number of available modes, the current mode, and the characteristics of each mode.
    • The parameter to the function is the mode to set. If it is -1, the mode is not changed. Otherwise it must be between 0 and N - 1, where N is the number of graphics modes supported. The parameter is not checked by this function, but the SetMode function checks it.
    • It uses Sys V x86-64 function calling conventions, except for calls to UEFI functions, which use the UEFI convention.
    • It uses a function called efi_printf, which works just like printf and writes to ConOut using EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
    • It relies on the startup code storing a pointer to the EFI Boot Services Table in a global variable named efi_boot_services.
    • It is written for gas rather than nasm.

    Here is sample output:

    max mode: 5
    mode 1: size 36, ver 0, hor res 800, ver res 600, pixel format 1
    frame buffer: b1000000, frame buffer size: 1d4c00
    mode 0: size 36, ver 0, hor res 640, ver res 480, pixel format 1
    mode 1: size 36, ver 0, hor res 800, ver res 600, pixel format 1
    mode 2: size 36, ver 0, hor res 1024, ver res 768, pixel format 1
    mode 3: size 36, ver 0, hor res 1280, ver res 1024, pixel format 1
    mode 4: size 36, ver 0, hor res 1600, ver res 1200, pixel format 1
    

    I assume you are familiar with UEFI, so I haven't explained how everything works, so let me know if you need more explanation.

                .intel_syntax noprefix
    
                .section .text
                .align  16
                .globl  gfxmode
        gfxmode:
                push    rbx
                push    rbp
                push    r14
                push    r15
                sub     rsp, 0x38
    
                mov     ebp, edi                // desired mode
    
                lea     rcx, EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID[rip]
                xor     edx, edx                // arg 2: unused
                lea     r8, 0x20[rsp]           // arg 3: address of protocol
                mov     rax, efi_boot_services[rip]
                call    0x140[rax]              // locate protocol
                test    rax, rax
                js      2f
    
                mov     r15, 0x20[rsp]          // graphics output protocol
                mov     r14, 0x18[r15]          // mode
    
                lea     rdi, trace1[rip]
                mov     esi, [r14]              // max mode
                call    efi_printf
    
                mov     rdi, 8[r14]             // current mode info
                mov     esi, 4[r14]             // current mode number
                mov     edx, 16[r14]            // current mode info size
                call    print_mode
    
                lea     rdi, trace3[rip]
                mov     rsi, 24[r14]            // frame buffer addr
                mov     rdx, 32[r14]            // frame buffer size
                call    efi_printf
    
                xor     ebx, ebx
        1:
                mov     rcx, r15                // arg 1: graphics output protocol
                mov     edx, ebx                // arg 2: mode number
                lea     r8, 0x30[rsp]           // arg 3: &info size
                lea     r9, 0x28[rsp]           // arg 4: &info
                call    0x00[rcx]               // query mode
                test    rax, rax
                js      2f
    
                mov     rdi, 0x28[rsp]          // mode info
                mov     esi, ebx                // mode number
                mov     edx, 0x30[rsp]          // mode info size
                call    print_mode
    
                mov     rax, efi_boot_services[rip]
                mov     rcx, 0x28[rsp]          // mode info
                call    0x48[rax]               // free pool
    
                inc     ebx
                cmp     ebx, [r14]              // max mode
                jb      1b
    
                xor     eax, eax
                test    ebp, ebp                // new mode
                js      2f
                mov     rcx, r15                // arg 1: graphics output protocol
                mov     edx, ebp                // arg 2: mode number
                call    0x08[rcx]               // set mode
    
        2:
                add     rsp, 0x38
                pop     r15
                pop     r14
                pop     rbp
                pop     rbx
                ret
    
                .align  16
        print_mode:
                // rdi: mode info
                // esi: mode number
                // edx: mode size
                mov     ecx, [rdi]              // mode version
                mov     r8d, 4[rdi]             // hor res
                mov     r9d, 8[rdi]             // ver res
                mov     eax, 12[rdi]            // pixel format
                push    rax
                lea     rdi, trace2[rip]
                call    efi_printf
                add     rsp, 8
                ret
    
        trace1: .asciz  "max mode: %d\n"
        trace2: .asciz  "mode %d: size %d, ver %d, hor res %d, ver res %d, pixel format %d\n"
        trace3: .asciz  "frame buffer: %p, frame buffer size: %llx\n"
    
                .align  16
        EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID:
                .byte   0xde,0xa9,0x42,0x90,0xdc,0x23,0x38,0x4a
                .byte   0x96,0xfb,0x7a,0xde,0xd0,0x80,0x51,0x6a