程序问答   发布时间:2022-06-02  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了在 C (UEFI) 中获取 EDID 信息:读取 ES:DI 寄存器?大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

如何解决在 C (UEFI) 中获取 EDID 信息:读取 ES:DI 寄存器??

开发过程中遇到在 C (UEFI) 中获取 EDID 信息:读取 ES:DI 寄存器?的问题如何解决?下面主要结合日常开发的经验,给出你关于在 C (UEFI) 中获取 EDID 信息:读取 ES:DI 寄存器?的解决方法建议,希望对你解决在 C (UEFI) 中获取 EDID 信息:读取 ES:DI 寄存器?有所启发或帮助;

我正在开发一个操作系统,我想从监视器中获取 EDID,我发现了一些汇编代码 (https://wiki.osdev.org/EDID) 以在 ES:DI 寄存器中获取 edID,

@H_775_5@mov ax,0x4f15
mov bl,0x01
xor cx,cx
xor dx,dx
int         0x10
;AL = 0x4F if function supported
;AH = status (0 is success,1 is fail)
;ES:DI contains the EDID
@H_301_5@

如何在 C 文件中获取 AL、AH 和 ES:DI 值?

@H_403_9@实际上我正在开发一个 64 位 UEFI 操作系统

LoadGDT:   
    lgdt [rdi]
    mov ax,0x10 
    mov ds,ax
    mov es,ax
    mov fs,ax
    mov gs,ax
    mov ss,ax
    pop rdi
    mov rax,0x08
    push rax
    push rdi
    retfq
GLOBAL LoadGDT
@H_301_5@

我能够运行这些上面的 asm 代码并使用 C 中的全局函数在 C 中获取它,

解决方法

osdev.org 上的那个页面包含要在 CPU 处于 16 位实模式时运行的代码。
您不仅可以通过所涉及的寄存器来判断,还可以通过使int 10h 的事实来判断。
This is a well-known BIOS interrupt service 是用 16 位实模式代码编写的。

如果您的目标是 UEFI,那么您的引导加载程序实际上是一个 UEFI 应用程序,它是一个 PE32(+) 映像。
如果 CPU 支持 64 位,固件将切换到长模式(64 位模式)并加载您的引导加载程序。
否则,它将切换到保护模式(32 位模式)。
无论如何,UEFI 中从不使用实模式。

您可以使用 GDT/LDT 中的 16 位代码段从保护/长模式调用 16 位代码,但不能调用实模式代码(即编写用于实模式分段的代码) ) 因为模式之间的分割工作完全不同。
另外,在实模式下,中断是通过 IVT 而不是 IDT 调度的,您需要获取中断 10h 的原始入口点。

UEFI 协议 EFI_EDID_DISCOVERED_PROTOCOL

幸运的是,UEFI 替代了传统 BIOS 接口提供的大多数基本服务。
在这种情况下,您可以使用 EFI_EDID_DISCOVERED_PROTOCOL 并最终通过使EFI_EDID_OVERRIDE_PROTOCOL 应用平台固件中的任何覆盖。

EFI_EDID_DISCOVERED_PROTOCOL 使用起来很简单,它只是一个 (Size,Data) 对。

typedef struct _EFI_EDID_DISCOVERED_PROTOCOL {
    UINT32   SizeOfEdid;
    UINT8   *Edid;
} EFI_EDID_DISCOVERED_PROTOCOL;

(来自gnu-efi)

缓冲区 Edid 的格式可以在 VESA 规范中找到,甚至可以在 Wikipedia 上找到。

例如,我使用 gnu-efix64_64-w64-mingw32(一个 GCC 版本和针对 PE 的工具)编写了一个简单的 UEFI 应用程序。 我避免使用 uefilib.h便仅将 gnu-efi 用于定义与 EUFI 相关的结构。

代码糟糕,它假设最多有 10 个句柄支持 EDID 协议,我只为 EDID 数据编写了部分结构(因为我很无聊)。
但这应该足够了。

注意我的 VM 没有返回任何 EDID 信息,所以代码没有完全测试!

#include <efi.h>

//You are better off using this lib
//#include <efilib.h>

EFI_GUID gEfiEdidDiscoveredProtocolGuid = EFI_EDID_DISCOVERED_PROTOCOL_GUID;
EFI_SYSTEM_TABLE* gST = NULL;

typedef struct _EDID14 {
    UINT8 Signature[8];
    UINT16 ManufacturerID;
    UINT16 ManufacturerCode;
    UINT32 serial;
    UINT8 Week;
    UINT8 Year;
    UINT8 Major;
    UINT8 Minor;
    UINT32 InputParams;
    UINT8 HSize;
    UINT8 VSize;
    UINT8 GAMMa;
    
    //...omitted...
} EDID14_RAW;

VOID Print(CHAR16* String)
{
    gST->ConOut->OutputString(gST->ConOut,String);
}

VOID PrintHex(UINT64 number)
{
    CHAR16* digits = L"0123456789abcdef";
    CHAR16 buffer[2] = {0,0};
    
    for (INTN i = 64-4; i >= 0; i-=4)
    {
        buffer[0] = digits[(number >> i) & 0xf];
        Print(buffer);
    }   
}

VOID PrintDec(UINT64 number)
{
    CHAR16 buffer[21] = {0};
    UINTN i = 19;
    
    do
    {
        buffer[i--] = L'0' + (number % 10);
        number = number / 10;
    }
    while (number && i >= 0);
    
    Print(buffer + i + 1);
}


#define MANUFACTURER_DECODE_LETTER(X)  ( L'A' + ( (X) & 0x1f ) - 1 )


EFI_STATUS efi_main(EFI_HANDLE ImageHandle,EFI_SYSTEM_TABLE* SystemTablE)
{
    EFI_STATUS Status = EFI_succesS;
    EFI_HANDLE EDIDHandles[10];
    UINTN Size = sizeof(EFI_HANDLE) * 10;
    EFI_EDID_DISCOVERED_PROTOCOL* EDID;
     
    gST = SystemTable;
    
    if ( EFI_ERROR( (Status = SystemTable->Bootservices->LocateHandle(ByProtocol,&gEfiEdidDiscoveredProtocolGuid,NULL,&Size,EDIDHandles)) ) )
    {
        Print(L"Failed to get EDID handles: "); PrintHex(Status); Print(L"\r\n");
        return Status;
    }

    for (INTN i = 0; i < Size/sizeof(EFI_HANDLE); i++)
    {
        if (EFI_ERROR( (SystemTable->Bootservices->OpenProtocol(
            EDIDHandles[i],(void**)&EDID,ImageHandle,EFI_OPEN_PROTOCOL_GET_PROTOCOL)) ) )
        {
            Print(L"Failed to get EDID info for handle "); PrintDec(i); Print(L": "); PrintHex(Status); Print(L"\r\n");
            return Status;
        }
        
        if (EDID->SizeOfEdid == 0 || EDID->Edid == NULL)
        {
            Print(L"No EDID data for handle "); PrintDec(i); Print(L"\r\n");
            conTinue;
        }
        
        /*
            THIS CODE IS NOT TESTED!
            ! ! !   D O   N O T   U S E  ! ! !
        */
        EDID14_RAW* EdidData = (EDID14_RAW*)EDID->Edid;
        
        CHAR16 Manufacturer[4] = {0};
        Manufacturer[0] = MANUFACTURER_DECODE_LETTER(EdidData->ManufacturerID >> 10);
        Manufacturer[1] = MANUFACTURER_DECODE_LETTER(EdidData->ManufacturerID >> 5);
        Manufacturer[2] = MANUFACTURER_DECODE_LETTER(EdidData->ManufacturerID);
        Print(L"Manufacturer ID: "); Print(Manufacturer); Print(L"\r\n");
        Print(L"Resolution: "); PrintDec(EdidData->HSizE); Print(L"X"); PrintDec(EdidData->VSizE); Print(L"\r\n");
        
    }
    return Status;
}

ACPI

如果您不想使用这些 UEFI 协议,您可以使用 ACPI。每个显示输出设备都有一个 _DDC 方法,该方法记录在 ACPI 规范中,可用于返回 EDID 数据(作为 128 或 256 字节的缓冲区)。
这种方法在概念上很简单,但实际上它需要编写一个完整的 ACPI 解析器(包括 AML VM),这是一项大量工作。
但是,ACPI 是现代操作系统所必需的,因此您可以稍后使用它来获取 EDID 数据,而无需担心 UEFI 协议。

大佬总结

以上是大佬教程为你收集整理的在 C (UEFI) 中获取 EDID 信息:读取 ES:DI 寄存器?全部内容,希望文章能够帮你解决在 C (UEFI) 中获取 EDID 信息:读取 ES:DI 寄存器?所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。