简体   繁体   中英

Retrieve list of VESA video modes from Int 10h/AX=4F00h

I am trying to develop a proof-of-concept os. Howsoever in the process one of the problems I encounter is the vesa video modes. It seems there is a lack of hardcoded video modes numbers after vesa told us to get them from vbe bios information and find the one that fits our needs. However I am unable to receive the video modes as I don't know how to use vbeFarPtr from C kernel in 32 bit

Here is my kernel code:

I passed a VbeInfoBlock as a parameter to the kernel from my second stage bootloader after receiving the information with int 0x10 ax=0x4f00


int kmain(struct VbeInfoBlock *vbeinfo)
{


    init_idt();
    SetPITSpeed(100);

    init_DTCursor();

    printf(vbeinfo->signature); // I can print VESA here means I have the vbeinfoblock

    char* str = "";

    itoa(vbeinfo->video_modes,str,16); // I want a hex dump so I convert it to hex

    printf(str); // I get "VESA" for the signature followed by a string "1053" and nothing else while the list should be like this
    // If for example video mode 0x0103, 0x0118 and 0x0115 are supported
    // The list should be as 03 01 15 01 18 01 FF FF
    // So I should atleast get some FF FF
    // My output is "VESA 1053"

    while(1);
}

The VbeInfoBlock is defined a follows if you dont know

struct VbeInfoBlock
    {
        char signature[4];  // must be "VESA" to indicate valid VBE support
        uint16_t version;           // VBE version; high byte is major version, low byte is minor version
        uint32_t oem;           // segment:offset pointer to OEM
        uint32_t capabilities;      // bitfield that describes card capabilities
        uint32_t video_modes;       // segment:offset pointer to list of supported video modes
        uint16_t video_memory;      // amount of video memory in 64KB blocks
        uint16_t software_rev;      // software revision
        uint32_t vendor;            // segment:offset to card vendor string
        uint32_t product_name;      // segment:offset to card model name
        uint32_t product_rev;       // segment:offset pointer to product revision
        char reserved[222];     // reserved for future expansion
        char oem_data[256];     // OEM BIOSes store their strings in this area
    } __attribute__ ((packed));

I couldn't understand the problem. Are there any other way of doing it? Or is my way correct but my code is incorrect?

I think the problem is that video_modes part in VbeInfoBlock is defined as a segment:offset pair. I don't know how to use it in 32 bit C code.

(You can request my second stage bootloader or my original bootloader but for this problem I think it is unnecessary)

EDIT:

The code I tried after Brendan's answer

    uint32_t physical_address = (vbeinfo->video_modes_segment << 4) + vbeinfo->video_modes_offset;

    uint16_t *videoListPointer = (uint16_t *)physical_address;
    char chr = '\0';
    while(*videoListPointer != 0xffff) {

        itoa(*videoListPointer,chr,16);
        printf(chr);
        videoListPointer++;
    }

and my gdt

gdt_start :
gdt_null : ; the mandatory null descriptor
dd 0x0 ; 'dd ' means define double word ( i.e. 4 bytes )
dd 0x0
gdt_code : 
dw 0xffff ; Limit ( bits 0 -15)
dw 0x0 ; Base ( bits 0 -15)
db 0x0 ; Base ( bits 16 -23)
db 10011010b ; 1st flags , type flags
db 11001111b ; 2nd flags , Limit ( bits 16 -19)
db 0x0 ; Base ( bits 24 -31)
gdt_data : 
dw 0xffff ; Limit ( bits 0 -15)
dw 0x0 ; Base ( bits 0 -15)
db 0x0 ; Base ( bits 16 -23)
db 10010010b ; 1st flags , type flags
db 11001111b ; 2nd flags , Limit ( bits 16 -19)
db 0x0 ; Base ( bits 24 -31)
gdt_end : 

gdt_descriptor :
dw gdt_end - gdt_start - 1 
dd gdt_start 

CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start   


Edit 2:

Image

Screenshot of myy output

Edit 3:

Code I used:


int kmain(struct VbeInfoBlock *vbeinfo)
{


    init_idt();
    SetPITSpeed(100);

    init_DTCursor();

    uint32_t physical_address = (vbeinfo->video_modes_segment << 4) + vbeinfo->video_modes_offset;

    uint16_t *videoListPointer = (uint16_t *)physical_address;
    char chr[9];

    while(*videoListPointer != 0xffff) {

        //itoa(*videoListPointer, chr,16);
        printf(*videoListPointer);
        videoListPointer++;

    }


    while(1);
}

and screenshot of my output without itoa

Edit4:

gcc -v

C:\Users\Asus>gcc -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=D:/MinGW/mingw32/bin/../libexec/gcc/i686-w64-mingw32/8.1.0/lto-wrapper.exe Target: i686-w64-mingw32 Configured with: ../../../src/gcc-8.1.0/configure --host=i686-w64-mingw32 --build=i686-w64-mingw32 --target=i686-w64-mingw32 --prefix=/mingw32 --with-sysroot=/c/mingw810/i686-810-win32-dwarf-rt_v6-rev0/mingw32 --enable-shared --enable-static --disable-multilib --enable-languages=c,c++,fortran,lto --enable-libstdcxx-time=yes --enable-threads=win32 --enable-libgomp --enable-libatomic --enable-lto --enable-graphite --enable-checking=release --enable-fully-dynamic-string --enable-version-specific-runtime-libs --disable-sjlj-exceptions --with-dwarf2 --disable-libstdcxx-pch --disable-libstdcxx-debug --enable-bootstrap --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --with-gnu-as --with-gnu-ld --with-arch=i686 --with-tune=generic --with-libiconv --with-system-z lib --with-gmp=/c/mingw810/prerequisites/i686-w64-mingw32-static --with-mpfr=/c/mingw810/prerequisites/i686-w64-mingw32-static --with-mpc=/c/mingw810/prerequisites/i686-w64-mingw32-static --with-isl=/c/mingw810/prerequisites/i686-w64-mingw32-static --with-pkgversion='i686-win32-dwarf-rev0, Built by MinGW-W64 project' --with-bugurl= https://sourceforge.net/projects/mingw-w64 CFLAGS='-O2 -pipe -fno-ident -I/c/mingw810/i686-810-win32-dwarf-rt_v6-rev0/mingw32/opt/include -I/c/mingw810/prerequisites/i686-zlib-static/include -I/c/mingw810/prerequisites/i686-w64-mingw32-static/include' CXXFLAGS='-O2 -pipe -fno-ident -I/c/mingw810/i686-810-win32-dwarf-rt_v6-rev0/mingw32/opt/include -I/c/mingw810/prerequisites/i686-zlib-static/include -I/c/mingw810/prerequisites/i686-w64-mingw32-static/include' CPPFLAGS=' -I/c/mingw810/i686-810-win32-dwarf-rt_v6-rev0/mingw32/opt/include -I/c/mingw810/prerequisites/i686-zlib-static/include -I/c/mingw810/prerequisites/i686-w64-mingw32 -static/include' LDFLAGS='-pipe -fno-ident -L/c/mingw810/i686-810-win32-dwarf-rt_v6-rev0/mingw32/opt/lib -L/c/mingw810/prerequisites/i686-zlib-static/lib -L/c/mingw810/prerequisites/i686-w64-mingw32-static/lib -Wl,--large-address-aware' Thread model: win32 gcc version 8.1.0 (i686-win32-dwarf-rev0, Built by MinGW-W64 project)

Edit5:

Screen shot of output without *

First, change your structure a little so that video_modes is split into 2 fields, like this:

struct VbeInfoBlock {
    char signature[4];  // must be "VESA" to indicate valid VBE support
    uint16_t version;           // VBE version; high byte is major version, low byte is minor version
    uint32_t oem;           // segment:offset pointer to OEM
    uint32_t capabilities;      // bitfield that describes card capabilities

    uint16_t video_modes_offset;
    uint16_t video_modes_segment;

    uint16_t video_memory;      // amount of video memory in 64KB blocks
    uint16_t software_rev;      // software revision
    uint32_t vendor;            // segment:offset to card vendor string
    uint32_t product_name;      // segment:offset to card model name
    uint32_t product_rev;       // segment:offset pointer to product revision
    char reserved[222];     // reserved for future expansion
    char oem_data[256];     // OEM BIOSes store their strings in this area
} __attribute__ ((packed));

Next, calculate the physical address of the list of video modes, like this:

uint32_t physical_address = (vbeinfo->video_modes_segment << 4) + vbeinfo->video_modes_offset;

Next, do whatever you have to to convert the physical address into a virtual address that can be used as pointer. If you're not using paging and segment register base addresses are zero then this will be trivial, like uint16_t *videoListPointer = (uint16_t *)physical_address; . If segment register bases are non-zero then you'll need to subtract them from the physical address (and make sure you use "32-bit unsigned" subtraction so that if the result would be negative it wraps around to a valid positive result). If paging is used then it's going to depend on how paging is used (eg maybe you map the physical page/s containing the video mode list at whatever virtual address you feel like).

In any case, once you have a usable pointer you can do something like:

    while(*videoListPointer != 0xFFFF) {
        printf("0x%04X\n", *videoListPointer);
        videoListPointer++;
    }

However; if that works you'll have a list of meaningless numbers (the old "fixed mode numbers" were deprecated and now any mode number can mean anything). You have to use "int 0x10, ax = 0x4F01, Get VBE mode information" to find out what the mode actually is (resolution, color depth, ...); and you can't do that in protected mode and will have to switch back to real mode for that.

Given that you will have to switch back to real mode to make sense of the mode numbers, it's likely to be easier to switch back to real mode and then iterate the list of mode numbers (using the real mode "segment and offset" that VBE gave you without any conversions).

This is an addendum to Brendan's answer. In your first edit you incorporated changes suggested by Brendan and did this:

uint32_t physical_address = (vbeinfo->video_modes_segment << 4) + \
                            vbeinfo->video_modes_offset;

uint16_t *videoListPointer = (uint16_t *)physical_address;
char chr = '\0';
while(*videoListPointer != 0xffff) {

    itoa(*videoListPointer,chr,16);
    printf(chr);
    videoListPointer++;
}

First of all char chr = '\0' only guarantees allocation of a single byte initialized to 0. You really need a buffer of characters big enough for the longest string that might be returned by itoa . For hexadecimal that is 9 characters which includes 8 hexadecimal digits and the NUL(\0) terminator. For the worst case of base 2 (binary) it is 33 characters including the NUL(\0) terminator. You can declare a buffer this way:

char buf[9];

You can pass that buffer to itoa . It is easier to read the video mode numbers if you place a space character between each. The modified code could look like this:

uint32_t physical_address = (vbeinfo->video_modes_segment << 4) + \
                            vbeinfo->video_modes_offset;

uint16_t *videoListPointer = (uint16_t *)physical_address;
char buf[9];

while(*videoListPointer != 0xffff) {        
    itoa(*videoListPointer, buf, 16);
    printf(buf);
    printf(" ");
    videoListPointer++;
}

Most Important : I didn't catch this mistake until I finally reviewed all your code on GitHub. Brendan suggested a correct change to break apart the video_modes member of the VBEInfoBlock structure by replacing:

    uint32_t video_modes; // segment:offset pointer to list of supported video mode

With:

    uint16_t video_modes_offset;
    uint16_t video_modes_segment;

Real mode segment:offset pairs are stored in memory with offset followed by segment. The problem is in GitHub you reversed offset and segment by doing this:

    uint16_t video_modes_segment; // segment:offset pointer to list of supported video modes
    uint16_t video_modes_offset;

When it should be:

    uint16_t video_modes_offset;  // segment:offset pointer to list of supported video modes
    uint16_t video_modes_segment;

Because of this bug the address you compute for the Video Mode List is wrong which results in producing an incorrect list.


If these changes are made the output should look similar to:

在此处输入图像描述

This looks like a proper list especially since the end of the list includes the EGA/VGA video modes:

 0 1 2 3 4 5 6 7 DEF 10 11 12 13 6A

Video modes 8 9 AB C are usually reserved or are not part of the standard EGA/VGA video modes supported by QEMU. Mode 6A stands out because that happens to be the standard VESA 800x600 16-bit color mode. Based on this I assume I am looking at a list that would be appropriate for QEMU.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM