大佬教程收集整理的这篇文章主要介绍了在 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-efi 和 x64_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,请注明来意。