Page 1 of 1

Access UEFI gop framebuffer in longmode

Posted: Sun May 01, 2022 6:04 pm
by vtimofei
I have bootstraped my kernel in longmode.
I am using multiboot version 1 with grub and I enabled the video flag.
I added the page where the framebuffer is to the page tables.
I passed the multiboot struct address to my C kernel.

From there I am trying to write to the framebuffer address but my kernel panics in qemu.
Also when I print the framebuffer address in gdb it says it cannot access the memory.

Code: Select all

(gdb) print fb
$2 = 0xfd000000 <error: Cannot access memory at address 0xfd000000>
The code that I use to add the framebuffer page tables is:

Code: Select all

setup_framebuffer_page_tables:
        /* Get the multiboot struct address */
        movl %ebx, %edx

        /*
        Offset to the framebuffer member. The offset of the framebuffer member is
        actually 84 and it size is 64 bits, but since we are currently in protected
        mode, the address cannot be bigger than 32 bits.
        */
        add $88, %edx
        movl (%edx), %edx

        /* L4 */
        movl %edx, %ecx
        shr $30, %ecx
        and $0b111111111, %ecx

        movl $page_table_l3_framebuffer, %eax
        orl $0b11, %eax
        movl %eax, page_table_l4(, %ecx, 8)

        /* L3 */
        movl %edx, %ecx
        shr $21, %ecx
        and $0b111111111, %ecx

        movl $page_table_l2_framebuffer, %eax
        orl $0b11, %eax
        movl %eax, page_table_l3_framebuffer(, %ecx, 8)

        /* Huge pages of size 2 MiBs */
        movl $0, %ecx
.loop2:

        movl $0x200000, %eax
        mul %ecx

        /* present, writable, hugepage */
        orl $0b10000011, %eax
        movl %eax, page_table_l2_framebuffer(, %ecx, 8)

        inc %ecx
        cmp $512, %ecx
        jne .loop2

        ret
I debugged the code and it seemed liked its adding the pages correctly, but I am not sure if I should also do something more, like edit the gdt64.

Essentially what I want is to have a working console as soon as possible in long mode.

Re: Access UEFI gop framebuffer in longmode

Posted: Sun May 01, 2022 8:22 pm
by Octocontrabass
vtimofei wrote:The offset of the framebuffer member is actually 84 and it size is 64 bits, but since we are currently in protected mode, the address cannot be bigger than 32 bits.
The Multiboot specification is correct, the offset of the framebuffer member truly is 88. The x86 architecture is little-endian, which means if you read 32 bits starting at offset 88 you will read bits 0-31 of the framebuffer member. You can read bits 32-63 at offset 92.

How does being in 32-bit mode stop you from doing the 64-bit math necessary to build your page tables? You're not going to access the framebuffer in protected mode, so it doesn't matter how many bits are in its address. (And if you did want to access it in protected mode, you just have to pick a new virtual address. PAE page tables allow all physical addresses to be mapped.)

Level 4 of the page tables is indexed by bits 39-47 of the virtual address. It looks like you're trying to use bits 30-31 instead...

You can use "info mem" and "info tlb" in the QEMU monitor to check your page tables. Make sure you use both! Some page table problems will only be visible in one and not the other.

Re: Access UEFI gop framebuffer in longmode

Posted: Mon May 02, 2022 8:01 am
by vtimofei
Octocontrabass wrote: Level 4 of the page tables is indexed by bits 39-47 of the virtual address. It looks like you're trying to use bits 30-31 instead...
You are right, my paging setup for the framebuffer location was incorrect.

The qemu console, and the commands `info mem` and `info tlb` helped a ton thanks.

Posting the working code below:

Code: Select all

setup_framebuffer_page_tables:
        /* Get the multiboot struct address */
        movl %ebx, %edx

        add $88, %edx
        movl (%edx), %edx

        /* L3 */
        movl %edx, %ecx
        shr $30, %ecx
        and $0b111111111, %ecx

        and $0xC0000000, %edx /* We will start mapping from this address */

        movl $page_table_l2_framebuffer, %eax
        orl $0b11, %eax
        movl %eax, page_table_l3(, %ecx, 8)

        /* Huge pages of size 2 MiBs */
        movl $0, %ecx
.loop2:

        movl $0x200000, %eax
        imul %ecx, %eax
        orl %edx, %eax

        /* present, writable, hugepage */
        orl $0b10000011, %eax
        movl %eax, page_table_l2_framebuffer(, %ecx, 8)

        inc %ecx
        cmp $512, %ecx
        jne .loop2

        ret