Search code examples
iox86uefireal-modevideo-card

Accessing intel graphics card registers through I/O space and MMIO in UEFI


I am trying to write a code sequence, that will switch my intel graphics card into the legacy VGA mode after a call to Exitbootservices() in my uefi nasm bootloader. In order to do that I am to change some values in my graphics card's registers. My graphics card supports two ways of accessing its registers - through MMIO and through I/O space via a pair of I/O registers called MMIO_ADDRESS and MMIO_DATA. Base address for the MMIO and base port for the I/O access are obtained through the PCI configuration space registers. I successfully got both of the values from the PCI configuration space. Weirdly, trying to read any register value through the MMIO(MMIO_BAR + reg_offset) or through the pair of I/O registers always returns zero. Trying to write to any of the device's registers with bot ways, listed above, also results in nothing. I would like to know, what I miss, because clearly I am missing something. My guess is that, the graphics card register access may be locked by some graphics card feature. By the way, my intel graphics card is operated by the linux i915 driver. I validated MMIO BAR and I/O port base multiple times by: using lspci -vvv in Linux, by calling the pci util in the UEFI shell and by simply reading the values from the pci configuration space with my bootloader.

This is the code I have so far. I call get_mmap(), then I call exitbootservices(). After that I change the caching policy for the VGA framebuffer by changing values in the fixed-range MTRRs. After that I put some pixels on the screen(I chechked the buffer address via GOP in another bootloader) to see that PC is not stalled after a call to exitbootservices(). After that I call a function, that should disable all current display modes of my intel graphics card. Then I put more pixels on the screen to see that computer is not stalled, though, pixels should not be visible and the first sequence of pixels, I printed earlier, is expected to disappear after I alter the graphics card's register values. After that I call a sequence of functions, that put a VGA compatible graphics card into the VGA 13h mode(256 colors, 320x200)(confirmed to work on real hardware in 16-bit real-mode bootloader). My code(jd9999_hdr_macro is an implementation of the UEFI PE header):

[BITS 64]
[DEFAULT ABS]
[ORG 0x00100000]

%include "jd9999_hdr_macro.inc"

jd9999_hdr_macro textsize, datasize, 0x00100000, textsize+datasize+1024



section .text follows=.header
   start:
   sub rsp, 6*8+8 ; Copied from Charles AP's implementation, fix stack alignment issue (Thanks Charles AP!)

      mov qword [EFI_HANDLE], rcx
      mov qword [EFI_SYSTEM_TABLE], rdx

      mov rax, qword [EFI_SYSTEM_TABLE]
      mov rax, qword [rax+96]
      mov rax, qword [rax+56]
      mov qword [get_mmap_boot_srvc], rax

      mov rax, qword [EFI_SYSTEM_TABLE]
      mov rax, qword [rax+64]
      mov rax, qword [rax+8]
      mov qword [efi_print], rax

      mov rax, qword [EFI_SYSTEM_TABLE]
      mov rax, qword [rax+96]
      mov rax, qword [rax+232]
      mov qword [exit_boot_services], rax


      mov rcx, mmap_sz
      mov rdx, MMap
      mov r8, mmkey
      mov r9, mmdsz
      mov r10, mmdsv     ; get_mmap
      sub rsp, 32
      call qword [get_mmap_boot_srvc]
      add rsp, 32

      mov rcx, qword [EFI_HANDLE]      ; EBS
      mov rdx, qword [mmkey]
      xor r8, r8
      sub rsp, 32
      call qword [exit_boot_services]
      add rsp, 32

      mov rdi, 0x00000000a0000000
      mov eax, 0x22822837           ; First sequence of pixels
      mov rcx, 0x2223
      cld
      rep stosd

      mov eax, 00000001000000010000000100000001b
      mov edx, 00000001000000010000000100000001b   ; Reprogram MTRRs
      mov ecx, 0x00000259
      wrmsr


      mov rdi, 0x00000000c2000000;
      call disable_all_display_modes   ; Here I pass the MMIO BAR in rdi and put some stuff into registers through it. After this call the screen should become blank and any further attempts to print any pixels to it should not display anything.

      mov rdi, 0x00000000a0000000 + 0x2223*4       ; Second sequence of pixels(should not be displayed)
      mov eax, 0x33722818
      mov rcx, 0x2223
      cld
      rep stosd



      mov rsi, VGA13h
      call set_regs
                                ; VGA 13h sequence
      mov rsi, palette256
      call set_palette256


      cld
      mov rcx, 64000
      mov rdi, 0xA0000
      mov al, 60                 ; Fill the screen in VGA 13h mode
      rep stosb

      cli
      hlt

; rdi - MMIO BAR
align 8
disable_all_display_modes:             ; A function that disables all current video modes through MMIO
    mov eax, dword [rdi+0x61140]
    and eax, 11011111111111111111111111111111b  
    mov dword [rdi+0x61140], eax                  ; Disable sDVO ports stall
    mov eax, dword [rdi+0x61160] 
    and eax, 11011111111111111111111111111111b
    mov dword [rdi+0x61160], eax


    mov eax, dword [rdi+0x70080]
    and eax, 11111111111111111111111111011000b  ; CURACNTR - cursor A disable
    mov dword [rdi+0x70080], eax

    mov eax, dword [rdi+0x700c0]
    and eax, 11111111111111111111111111011000b  ; CURBCNTR - cursor B disable
    mov dword [rdi+0x700c0], eax

    mov eax, dword [rdi+0x70180]
    and eax, 01111111111111111111111111111111b  ; DSPACNTR - Plane A disable
    mov dword [rdi+0x70180], eax

    mov eax, dword [rdi+0x71180]
    and eax, 01111111111111111111111111111111b  ; DSPBCNTR - Plane B disable
    mov dword [rdi+0x71180], eax


    mov eax, dword [rdi+0x70008]
    or eax, 00000000000011000000000000000000b   ; PIPEACONF - Disable all planes and cursors
    mov dword [rdi+0x70008], eax
    mov eax, dword [rdi+0x70008]
    ;test eax, 10000000000000000000000000000000b
    ;jz ._PIPEA_skip_wait
    and eax, 01111111111111111111111111111111b  ; PIPEACONF - Disable PIPE
    mov dword [rdi+0x70008], eax
    ;lea eax, [edi+0x70008]
    ;monitor    ; this is probably the proper way of waiting for the pipes to disable
    ;mwait
    ;._PIPEA_skip_wait:

    mov eax, dword [rdi+0x71008]
    or eax, 00000000000011000000000000000000b   ; PIPEBCONF - Disable all planes and cursors
    mov dword [rdi+0x71008], eax
    mov eax, dword [rdi+0x71008]
    ;test eax, 10000000000000000000000000000000b
    ;jz ._PIPEB_skip_wait
    and eax, 01111111111111111111111111111111b  ; PIPEBCONF - Disable PIPE
    mov dword [rdi+0x71008], eax
    ;lea eax, [edi+0x71008]
    ;monitor    ; this is probably the proper way of waiting for the pipes to disable
    ;mwait
    ;._PIPEB_skip_wait:    


    mov eax, dword [rdi+0x71400]
    and eax, 01111111111111111111111111111111b  ; Disable VGA display
    mov dword [rdi+0x71400], eax

    mov eax, dword [rdi+0x68000]
    and eax, 11111111111111111111011111111111b  ; Disable panel fitter
    mov dword [rdi+0x68000], eax


    mov eax, dword [rdi+0x6014]
    and eax, 01101111111111111111111111111111b  ; DPLLA_CTRL - DPLL VCO = 0(disabled), VGA mode = 0 (enabled)
    mov dword [rdi+0x6014], eax

    mov eax, dword [rdi+0x6018]
    and eax, 01101111111111111111111111111111b  ; DPLLB_CTRL - DPLL VCO = 0(disabled), VGA mode = 0 (enabled)
    mov dword [rdi+0x6018], eax

ret


align 8
; dx - IOBAR                    
disable_all_display_modes_IO:  ; A function that disables all current video modes through I/O
   mov eax, 0x61140
   out dx, eax
   add dx, 4
   in eax, dx
   and eax, 11011111111111111111111111111111b
   out dx, eax
   sub dx, 4
   mov eax, 0x61160
   out dx, eax           ; Disable sDVO ports stall
   add dx, 4
   in eax, dx
   and eax, 11011111111111111111111111111111b
   out dx, eax
   sub dx, 4
   
   mov eax, 0x70080
   out dx, eax
   add dx, 4             ; CURACNTR - cursor A disable
   in eax, dx                
   and eax, 11111111111111111111111111011000b
   out dx, eax
   sub dx, 4

   mov eax, 0x700c0
   out dx, eax
   add dx, 4              ; CURBCNTR - cursor B disable
   in eax, dx      
   and eax, 11111111111111111111111111011000b
   out dx, eax
   sub dx, 4

   mov eax, 0x70180
   out dx, eax
   add dx, 4              ; DSPACNTR - Plane A disable
   in eax, dx  
   and eax, 01111111111111111111111111111111b
   out dx, eax
   sub dx, 4

   mov eax, 0x71180
   out dx, eax
   add dx, 4              ; DSPBCNTR - Plane B disable
   in eax, dx
   and eax, 01111111111111111111111111111111b
   out dx, eax
   sub dx, 4

   mov eax, 0x70008
   out dx, eax
   add dx, 4
   in eax, dx
   or eax, 00000000000011000000000000000000b   ; PIPEACONF - Disable all planes and cursors 
   out dx, eax
   sub dx, 4  

   mov eax, 0x71008
   out dx, eax
   add dx, 4
   in eax, dx
   or eax, 00000000000011000000000000000000b   ; PIPEBCONF - Disable all planes and cursors 
   out dx, eax
   sub dx, 4

   mov eax, 0x71400
   out dx, eax
   add dx, 4
   in eax, dx
   and eax, 01111111111111111111111111111111b  ; Disable VGA display   
   out dx, eax
   sub dx, 4

   mov eax, 0x68000
   out dx, eax
   add dx, 4
   in eax, dx
   and eax, 11111111111111111111011111111111b  ; Disable panel fitter
   out dx, eax
   sub dx, 4

   mov eax, 0x6014
   out dx, eax
   add dx, 4
   in eax, dx
   and eax, 01101111111111111111111111111111b  ; DPLLA_CTRL - DPLL VCO = 0(disabled), VGA mode = 0 (enabled)
   out dx, eax
   sub dx, 4

   mov eax, 0x6018
   out dx, eax
   add dx, 4
   in eax, dx
   and eax, 01101111111111111111111111111111b  ; DPLLB_CTRL - DPLL VCO = 0(disabled), VGA mode = 0 (enabled)
   out dx, eax
   sub dx, 4

ret


align 8
set_regs:             ; Set VGA registers for the 13h mode
     xor rdx, rdx
     xor rax, rax
     xor rcx, rcx
     cli
     cld
     mov     dx, 0x3C2
     outsb

     mov     dx, 0x3DA
     outsb

     xor     cx, cx
     mov     dx, 0x3C4
    .loop_CRTC_:
         lodsb
         xchg    al, ah
         mov     al, cl
         out     dx, ax
         inc     cx
         cmp     cl, 4
     jbe     .loop_CRTC_

     mov     dx, 0x3D4
     mov     ax, 0x0E11
     out     dx, ax

     xor     cx, cx
     mov     dx, 0x3D4


    .loop_CRTC_2:
         lodsb
         xchg    al, ah
         mov     al, cl
         out     dx, ax
         inc     cx
         cmp     cl, 0x18
     jbe     .loop_CRTC_2

     xor     cx, cx
     mov     dx, 0x3CE
    .loop_GC_:
         lodsb
         xchg    al, ah
         mov     al, cl
         out     dx, ax
         inc     cx
         cmp     cl, 8
     jbe     .loop_GC_

     mov     dx, 0x3DA
     in      al, dx

     xor     cx, cx
     mov     dx, 0x3C0
    .l4:
         in      ax, dx
         mov     al, cl
         out     dx, al
         outsb
         inc     cx
         cmp     cl, 0x14
     jbe     .l4

     mov     al, 0x20
     out     dx, al
     sti
ret

align 8 
set_palette256:          ; Set Vga palette
     xor rdx, rdx
     xor rax, rax
     xor rcx, rcx
     cld
    .loop_:
         mov     dx, 0x03C8
         out     dx, al               ; output index                 
         inc     dx                   ; port 0x3C9
         mov cx, 3
         ;rep outsb
         outsb                        ; red  
         outsb                        ; blue
         outsb                        ; green
         inc     ax
         cmp     ax, 256
         jl      .loop_
ret



times 2048 - ($-$$) db 0 ;alignment
   textsize equ $-$$

section .data follows=.text
dataStart:

tststr dw __utf16__(`test_\0`)
numretstr dw __utf16__(`0x0000000000000000\n\0`)

   ;Handover variables
   EFI_HANDLE dq 0
   EFI_SYSTEM_TABLE dq 0

   get_mmap_boot_srvc dq 0
   efi_print dq 0
   exit_boot_services dq 0

memmap_UEFI:
   type dd 0
   phys_addr dq 0
   virt_addr dq 0
   num_pafes dq 0
   attribute dq 0

mmap_sz dq 4096

mmdsz dq 48
mmkey dq 0
mmdsv dq 0






       VGA13h        db   0x63, 0x00, 0x03, 0x01, 0x0F, 0x00, 0x0E, 0x5F, 0x4F
                         db   0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x41, 0x00
                         db   0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0x0E, 0x8F, 0x28
                         db   0x40, 0x96, 0xB9, 0xA3, 0xFF, 0x00, 0x00, 0x00, 0x00
                         db   0x00, 0x40, 0x05, 0x0F, 0xFF, 0x00, 0x01, 0x02, 0x03
                         db   0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C
                         db   0x0D, 0x0E, 0x0F, 0x41, 0x00, 0x0F, 0x00, 0x00


    palette256      db   00, 00, 00, 00, 10, 41, 12, 28, 18, 02, 43, 22, 35
                         db   19, 09, 58, 00, 00, 57, 35, 12, 43, 43, 47, 24, 24
                         db   28, 20, 24, 60, 10, 60, 15, 31, 47, 63, 62, 56, 20
                         db   60, 56, 22, 63, 61, 36, 63, 63, 63, 00, 00, 00, 05
                         db   05, 05, 08, 08, 08, 11, 11, 11, 14, 14, 14, 17, 17
                         db   17, 20, 20, 20, 24, 24, 24, 28, 28, 28, 32, 32, 32
                         db   36, 36, 36, 40, 40, 40, 45, 45, 45, 50, 50, 50, 56
                         db   56, 56, 63, 63, 63, 13, 12, 15, 15, 16, 22, 17, 20
                         db   29, 19, 24, 36, 21, 28, 43, 23, 31, 50, 25, 34, 57
                         db   26, 42, 63, 00, 15, 02, 01, 22, 04, 02, 29, 06, 03
                         db   36, 08, 04, 43, 10, 05, 50, 12, 06, 57, 14, 20, 63
                         db   40, 18, 06, 07, 25, 12, 11, 33, 17, 14, 40, 23, 18
                         db   48, 28, 21, 55, 34, 25, 62, 39, 27, 63, 48, 36, 15
                         db   03, 02, 22, 06, 04, 29, 09, 06, 36, 12, 08, 43, 15
                         db   10, 50, 18, 12, 57, 21, 14, 63, 28, 20, 15, 00, 00
                         db   22, 07, 00, 29, 15, 00, 36, 23, 00, 43, 31, 00, 50
                         db   39, 00, 57, 47, 00, 63, 55, 00, 15, 05, 03, 22, 11
                         db   07, 29, 17, 11, 36, 23, 15, 43, 29, 19, 50, 35, 23
                         db   57, 41, 27, 63, 53, 34, 28, 14, 12, 33, 20, 14, 38
                         db   26, 16, 43, 32, 18, 48, 38, 20, 53, 44, 22, 58, 50
                         db   24, 63, 56, 30, 05, 05, 06, 10, 10, 13, 15, 15, 20
                         db   20, 20, 27, 25, 25, 34, 30, 30, 41, 35, 35, 48, 44
                         db   44, 63, 03, 06, 05, 05, 11, 09, 07, 16, 13, 09, 21
                         db   17, 11, 26, 21, 13, 31, 25, 15, 36, 29, 20, 48, 38
                         db   06, 06, 07, 13, 13, 15, 20, 20, 23, 27, 27, 31, 34
                         db   34, 39, 41, 41, 47, 48, 48, 55, 57, 57, 63, 06, 15
                         db   04, 12, 22, 08, 18, 29, 12, 24, 36, 16, 30, 43, 20
                         db   36, 50, 24, 42, 57, 28, 54, 63, 35, 15, 10, 10, 22
                         db   16, 16, 29, 21, 21, 36, 27, 27, 43, 32, 32, 50, 38
                         db   38, 57, 43, 43, 63, 54, 54, 15, 15, 06, 22, 22, 12
                         db   29, 29, 18, 36, 36, 24, 43, 43, 30, 50, 50, 36, 57
                         db   57, 42, 63, 63, 54, 02, 04, 14, 06, 12, 21, 10, 20
                         db   28, 14, 28, 35, 18, 36, 42, 22, 44, 49, 26, 52, 56
                         db   36, 63, 63, 18, 04, 14, 24, 08, 21, 31, 12, 28, 37
                         db   16, 35, 44, 20, 42, 50, 24, 49, 57, 28, 56, 63, 38
                         db   63, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
                         db   00, 00, 00, 00, 00, 00, 00, 00, 00, 53, 44, 22, 09
                         db   08, 12, 16, 14, 16, 22, 21, 20, 29, 27, 24, 35, 34
                         db   28, 42, 40, 32, 48, 47, 36, 57, 56, 43, 08, 12, 16
                         db   14, 16, 22, 21, 20, 29, 27, 24, 35, 34, 28, 42, 40
                         db   32, 48, 47, 36, 57, 56, 43, 63, 13, 09, 11, 21, 16
                         db   15, 27, 22, 18, 36, 29, 22, 42, 35, 25, 51, 42, 29
                         db   57, 48, 32, 63, 56, 39, 06, 14, 09, 12, 21, 14, 18
                         db   27, 22, 24, 33, 28, 30, 39, 36, 36, 46, 42, 42, 52
                         db   47, 50, 59, 53, 00, 00, 00, 00, 00, 00, 00, 00, 00
                         db   00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
                         db   00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
                         db   00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
                         db   00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
                         db   00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
                         db   00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
                         db   00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
                         db   00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
                         db   00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
                         db   00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
                         db   00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
                         db   00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
                         db   00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
                         db   00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00
                         db   00



times 1024 - ($-$$) db 0 ;alignment

MMap:
    times 4096 db 0

    datasize equ $-$$

Photos of the computer screen after the program finished(from afar and a closeup with explanation): From afar: Closeup with explanation

As you can see, VGA 13h draws some weird stuff on the screen because previous graphics mode was not disabled. So, why does my code does not disable the current video mode of my intel graphics card(considering that the Base address of MMIO is alright)?.


Solution

  • Your reads from MMIO space return 0 because you are reading from the render and media section from the MMIO, which is not even powered on. But you don't want to read from the render and media section. You want to read from the display engine section. I just made exactly the same error.

    Here is an example from your code:

    mov eax, 0x70080
    out dx, eax
    add dx, 4 ; CURACNTR - cursor A disable
    in eax, dx

    Let's translate this to pseudocode:

    out("address channel", 0x70080)
    eax = in("data channel")

    The documentation says the following about the 2 example registers "CURACNTR" and "GTLC wake control":

    CURACNTR is at memory offset address 7 00 80h
    GTLC wake control is at MMIO address offset 13 00 90h

    What you need is the offset from the beginning of the MMIO space. The offset from the beginning of the MMIO space for the 2 example registers "CURACNTR" and "GTLC wake control" is:

    • CURACNTR: 18 00 00h + 7 00 80h = 1F 00 80h
    • GTLC wake control: 13 00 90h

    enter image description here

    This additional offset (18 00 00h) was taken from the Valleyview/Baytrail-documentation. With your graphics circuit you might have a different offset.