1

我正在尝试开发一个概念验证操作系统。但是,在此过程中,我遇到的问题之一是 vesa 视频模式。在 vesa 告诉我们从 vbe bios 信息中获取它们并找到适合我们需要的视频模式编号之后,似乎缺少硬编码的视频模式编号。但是我无法接收视频模式,因为我不知道如何vbeFarPtr在 32 位的 C 内核中使用

这是我的内核代码:

VbeInfoBlock在接收到 int 0x10 ax=0x4f00 的信息后,我将 a 作为参数从我的第二阶段引导加载程序传递给内核


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);
}

如果您不知道,则 VbeInfoBlock 定义如下

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));

我无法理解这个问题。还有其他方法吗?还是我的方式正确但我的代码不正确?

我认为问题在于 video_modes 部分VbeInfoBlock被定义为一个段:偏移量对。我不知道如何在 32 位 C 代码中使用它。

(您可以请求我的第二阶段引导加载程序或我原来的引导加载程序,但对于这个问题,我认为没有必要)

编辑:

我在 Brendan 回答后尝试的代码

    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++;
    }

还有我的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   


编辑2:

图片

myy 输出截图

编辑3:

我使用的代码:


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);
}

和没有 itoa 的我的输出截图

编辑4:

gcc -v

C:\Users\Asus>gcc -v 使用内置规范。COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=D:/MinGW/mingw32/bin/../libexec/gcc/i686-w64-mingw32/8.1.0/lto-wrapper.exe 目标:i686-w64-mingw32 配置:../。 ./../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,https://sourceforge.net/projects/mingw-w64CFLAGS='-O2 -pipe -fno-ident -I/c/mingw810/i686-810-win32-dwarf-rt_v6-rev0/mingw32/opt/include -I/c/mingw810/prerequisites/i686-zlib-static/包括 -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/包括'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/先决条件/i686-w64-mingw32-static/lib -Wl,--large-address-aware' 线程模型:win32 gcc version 8.1.0 (i686-win32-dwarf-rev0, Built by MinGW-W64 project)

编辑5:

不带 * 的输出屏幕截图

4

2 回答 2

3

首先,稍微改变一下你的结构,video_modes让它分成 2 个字段,如下所示:

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));

接下来,计算视频模式列表的物理地址,如下所示:

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

接下来,尽您所能将物理地址转换为可用作指针的虚拟地址。如果您不使用分页并且段寄存器基地址为零,那么这将是微不足道的,例如uint16_t *videoListPointer = (uint16_t *)physical_address;. 如果段寄存器基数不为零,那么您需要从物理地址中减去它们(并确保使用“32 位无符号”减法,以便如果结果为负,它会环绕为有效的正结果) . 如果使用分页,那么它将取决于如何使用分页(例如,也许您将包含视频模式列表的物理页面映射到您喜欢的任何虚拟地址)。

在任何情况下,一旦你有了一个可用的指针,你就可以执行以下操作:

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

然而; 如果可行,您将获得一个无意义数字列表(旧的“固定模式编号”已被弃用,现在任何模式编号都可以表示任何含义)。您必须使用“int 0x10, ax = 0x4F01, Get VBE mode information”来找出实际的模式是什么(分辨率,颜色深度,...);而且您不能在保护模式下执行此操作,并且必须为此切换回实模式。

鉴于您必须切换回实模式才能理解模式编号,切换回实模式然后迭代模式编号列表可能会更容易(使用 VBE 的实模式“段和偏移”给你没有任何转换)。

于 2019-10-10T19:53:57.257 回答
0

这是 Brendan 答案的附录。在您的第一次编辑中,您合并了 Brendan 建议的更改并执行了以下操作:

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++;
}

首先,char chr = '\0'仅保证分配一个初始化为 0 的字节。您确实需要一个足够大的字符缓冲区,以容纳可能由itoa. 对于包含 8 个十六进制数字和 NUL(\0) 终止符的 9 个字符的十六进制。对于基数 2(二进制)的最坏情况,它是 33 个字符,包括 NUL(\0) 终止符。你可以这样声明一个缓冲区:

char buf[9];

您可以将该缓冲区传递给itoa. 如果space在每个之间放置一个字符,则更容易阅读视频模式编号。修改后的代码可能如下所示:

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++;
}

最重要的:直到我最终查看了您在 GitHub 上的所有代码,我才发现这个错误。Brendan 建议进行正确的更改,通过替换来分解结构的video_modes成员:VBEInfoBlock

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

和:

    uint16_t video_modes_offset;
    uint16_t video_modes_segment;

实模式段:偏移量对存储在内存中,偏移量后跟段。问题出在 GitHub 中,您通过执行以下操作反转了偏移量和分段:

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

什么时候应该:

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

由于这个错误,您为视频模式列表计算的地址是错误的,这会导致生成不正确的列表。


如果进行了这些更改,则输出应类似于:

在此处输入图像描述

这看起来像是一个正确的列表,特别是因为列表的末尾包括 EGA/VGA 视频模式:

0 1 2 3 4 5 6 7 D E F 10 11 12 13 6A

视频模式8 9 A B C通常是保留的,或者不是 QEMU 支持的标准 EGA/VGA 视频模式的一部分。模式6A脱颖而出,因为它恰好是标准的 VESA 800x600 16 位颜色模式。基于此,我假设我正在查看适合 QEMU 的列表。

于 2019-10-23T16:17:15.927 回答