菜单

kaoqi
kaoqi
发布于 2026-08-24 / 9 阅读
0
0

GOP的系统表调用

紧接着上一篇文章,这里开始讲怎么从efi的main函数出发,进行GOP的系统调用。

主要目标是从基本的系统调用表开始,由GUID进行locateProtocol后,直接给一个pixel填满整个屏幕。本文暂时不追求terminal、渲染器这些东西的实现,就追求一个最基本的屏幕染色的功能。

调用主要流程

裸机代码编程可以参考之前写的文章:

http://kaoqi.xyz/archives/uefixi-tong-biao-tou-wen-jian

起码,我们解决了main函数的一些基本问题。

这次任务的主要流程,就是调用GOP这个Protocol中的BLT中的EfiBltVideoFill的枚举,将一个具体的pixel实现整个屏幕填充。

这包括如下的一些主要流程:

1、根据输入的EFI系统表,解析出boot services后输入GOP的GUID调用LocateProtocol函数并获取对应的协议指针

2、根据BS查到的协议指针,使用EFI_GRAPHICS_OUTPUT_PROTOCOL结构解析并获取协议中的Mode指针

3、根据Mode指针,使用EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE结构体获取帧基地址后,直接进行内存上帧基地址的修改可以直接触发对屏幕显示的更改,这样就直接完成任务了。

4、根据Mode指针,获取到一系列信息后,可以在PROTOCOL层级中调用BLT函数,使用EfiBltVideoFill方式进行单PIXEL的填充。这个过程相当于调用了UEFI固件提供给你的能力。

基础数据类型定义

主要定义从8到64不同位宽的数据格式

typedef unsigned long long UINT64;
typedef unsigned int UINT32;
typedef unsigned short UINT16;
typedef unsigned char UINT8;
typedef UINT64 UINTN;
typedef INT64 INTN;
typedef void VOID;

EFI系统调用表与GUID相关的结构体

定义与EFI相关的基本信息,也包括用于检索的GUID相关的内容。

杂项部分:

typedef UINT32 EFI_STATUS;
typedef VOID* EFI_HANDLE;

GUID结构体:

//GUID的结构体
typedef struct {
    UINT32  Data1;
    UINT16  Data2;
    UINT16  Data3;
    UINT8   Data4;
} EFI_GUID;

系统表部分结构体:(注意,boot services在更下面定义的)

//系统表表头的结构体
typedef struct {
  UINT64      Signature;
  UINT32      Revision;
  UINT32      HeaderSize;
  UINT32      CRC32;
  UINT32      Reserved;
} EFI_TABLE_HEADER;

//系统表本表结构体
typedef struct {
  EFI_TABLE_HEADER                 Hdr;
  CHAR16                           *FirmwareVendor;
  UINT32                           FirmwareRevision;
  EFI_HANDLE                       ConsoleInHandle;
  EFI_SIMPLE_TEXT_INPUT_PROTOCOL   *ConIn;
  EFI_HANDLE                       ConsoleOutHandle;
  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *ConOut;
  EFI_HANDLE                       StandardErrorHandle;
  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  *StdErr;
  EFI_RUNTIME_SERVICES             *RuntimeServices;
  EFI_BOOT_SERVICES                *BootServices;
  UINTN                            NumberOfTableEntries;
  EFI_CONFIGURATION_TABLE          *ConfigurationTable;
} EFI_SYSTEM_TABLE;

启动服务表结构体由于过于长,给作者干力竭了,所以使用额外的一篇文章来描述。

描述的文件在这里:

http://kaoqi.xyz/archives/uefizhong-de-boot-servicebiao-jie-gou

在本文中,对于GOP协议,主要关心的是handle的调用获取API,所以下面放一个简化版的struct:

// 首先,boot service的表拥有一个完全一样的表头
typedef struct {
  UINT64      Signature;
  UINT32      Revision;
  UINT32      HeaderSize;
  UINT32      CRC32;
  UINT32      Reserved;
} EFI_TABLE_HEADER;

// 这是给定的LocateProtocol函数定义
typedef
EFI_STATUS
(*EFI_LOCATE_PROTOCOL) (
  EFI_GUID                            *Protocol,
  VOID                                *Registration OPTIONAL,
  VOID                                **Interface
 );

// 然后定义BS表本身,注意,其中大部分的指针都被忽略掉了
typedef struct {
  EFI_TABLE_HEADER                 Hdr;
  UINT64                           _buf[37]; // 在Locate之前存在37个指针函数方法
  EFI_LOCATE_PROTOCOL              LocateProtocol;  // 这是我们需要的BS服务函数
  UINT64                           _buf[5];  //  在Locate之后还有5个或6个函数
} EFI_BOOT_SERVICES;

GOP相关的结构体

GOP协议接口相关与协议内部的数据结构:

//GUID的具体的值
static const EFI_GUID gEfiGraphicsOutputProtocolGuid = {
    0x9042a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a}
};

//PIXEL的枚举,由于UEFI原文里也没说这里具体应该对应啥,所以结合GRUB2的源码的话应该就这么枚举就OK
typedef enum {
    PixelRedGreenBlueReserved8BitPerColor,
    PixelBlueGreenRedReserved8BitPerColor,
    PixelBitMask,
    PixelBltOnly,
    PixelFormatMax
} EFI_GRAPHICS_PIXEL_FORMAT;

//mode指针的下级指针struct
typedef struct {
    UINT32 Version;
    UINT32 HorizontalResolution;
    UINT32 VerticalResolution;
    EFI_GRAPHICS_PIXEL_FORMAT PixelFormat;
    EFI_PIXEL_BITMASK PixelInformation;
    UINT32 PixelsPerScanLine;
} EFI_GRAPHICS_OUTPUT_MODE_INFORMATION;

//mode指针本体struct,可以在获取到GOP协议句柄后调用mode指针,完成解析
typedef struct {
    UINT32 MaxMode;
    UINT32 Mode;
    EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
    UINTN SizeOfInfo;
    UINT64 FrameBufferBase;
    UINTN FrameBufferSize;
} EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE;

//GOP协议的4个主要部分其中之一
typedef EFI_STATUS (EFIAPI *EFI_GRAPHICS_OUTPUT_PROTOCOL_QUERY_MODE) (
    EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
    UINT32 ModeNumber,
    UINTN *SizeOfInfo,
    EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info
);

//GOP协议的4个主要部分其中之二
typedef EFI_STATUS (EFIAPI *EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE) (
    EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
    UINT32 ModeNumber
);

//GOP协议的4个主要部分其中之三,也是最重要的BLT部分
typedef EFI_STATUS (EFIAPI *EFI_GRAPHICS_OUTPUT_PROTOCOL_BLT) (
    EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
    VOID *BltBuffer, OPTIONAL
    EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
    UINTN SourceX,
    UINTN SourceY,
    UINTN DestinationX,
    UINTN DestinationY,
    UINTN Width,
    UINTN Height,
    UINTN Delta OPTIONAL
);

//GOP协议由Locate找到后的本体struct
struct _EFI_GRAPHICS_OUTPUT_PROTOCOL {
    EFI_GRAPHICS_OUTPUT_PROTOCOL_QUERY_MODE QueryMode;
    EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE SetMode;
    EFI_GRAPHICS_OUTPUT_PROTOCOL_BLT Blt;
    EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode;
};

最终main函数代码参考

这里声明一下,下面这段代码中缺失了很多关于UEFI中系统表、BS表的struct描述,所以直接跑跑不通,需要结合进一步的实验来验证,这个后续专门出一篇文章来讲。


/**
 * UEFI Application to fill the screen with Red color using GOP Protocol.
 * This code assumes it is compiled within a UEFI development environment (like EDK II)
 * or linked against standard UEFI headers. Since the prompt asks for a single C file
 * without external libraries and allows defining structs, we define the necessary
 * UEFI types and protocols here.
 * 
 * Note: In a real UEFI environment, the entry point is usually `UefiMain` or `efi_main`,
 * and `gBS` (Boot Services) is provided by the environment. Here we simulate the structure.
 */

// --- Basic UEFI Type Definitions ---
typedef unsigned long long UINT64;
typedef unsigned int UINT32;
typedef unsigned short UINT16;
typedef unsigned char UINT8;
typedef UINT64 UINTN; // Assuming 64-bit UEFI
typedef INT64 INTN;
typedef void VOID;
typedef UINT32 EFI_STATUS;
typedef VOID* EFI_HANDLE;
typedef GUID EFI_GUID; // Simplified, usually defined as struct

#define EFI_SUCCESS 0
#define EFI_ERROR(x) ((INTN)(x) < 0)

// --- GUID Definition for GOP ---
static const EFI_GUID gEfiGraphicsOutputProtocolGuid = {
    0x9042a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a}
};

// --- Struct Definitions based on UEFI Spec ---

typedef enum {
    PixelRedGreenBlueReserved8BitPerColor,
    PixelBlueGreenRedReserved8BitPerColor,
    PixelBitMask,
    PixelBltOnly,
    PixelFormatMax
} EFI_GRAPHICS_PIXEL_FORMAT;

typedef struct {
    UINT32 Version;
    UINT32 HorizontalResolution;
    UINT32 VerticalResolution;
    EFI_GRAPHICS_PIXEL_FORMAT PixelFormat;
    EFI_PIXEL_BITMASK PixelInformation; // Only valid if PixelFormat == PixelBitMask
    UINT32 PixelsPerScanLine;
} EFI_GRAPHICS_OUTPUT_MODE_INFORMATION;

typedef struct {
    UINT32 MaxMode;
    UINT32 Mode;
    EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
    UINTN SizeOfInfo;
    UINT64 FrameBufferBase;
    UINTN FrameBufferSize;
} EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE;

typedef struct _EFI_GRAPHICS_OUTPUT_PROTOCOL EFI_GRAPHICS_OUTPUT_PROTOCOL;

typedef EFI_STATUS (EFIAPI *EFI_GRAPHICS_OUTPUT_PROTOCOL_QUERY_MODE) (
    IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
    IN UINT32 ModeNumber,
    OUT UINTN *SizeOfInfo,
    OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info
);

typedef EFI_STATUS (EFIAPI *EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE) (
    IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
    IN UINT32 ModeNumber
);

typedef EFI_STATUS (EFIAPI *EFI_GRAPHICS_OUTPUT_PROTOCOL_BLT) (
    IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
    IN VOID *BltBuffer, OPTIONAL
    IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
    IN UINTN SourceX,
    IN UINTN SourceY,
    IN UINTN DestinationX,
    IN UINTN DestinationY,
    IN UINTN Width,
    IN UINTN Height,
    IN UINTN Delta OPTIONAL
);

struct _EFI_GRAPHICS_OUTPUT_PROTOCOL {
    EFI_GRAPHICS_OUTPUT_PROTOCOL_QUERY_MODE QueryMode;
    EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE SetMode;
    EFI_GRAPHICS_OUTPUT_PROTOCOL_BLT Blt;
    EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *Mode;
};

// --- Boot Services Definition (Simplified) ---
typedef struct {
    // ... other services
    EFI_STATUS (EFIAPI *LocateProtocol) (
        IN EFI_GUID *Protocol,
        IN VOID *Registration, OPTIONAL
        OUT VOID **Interface
    );
    // ... other services
} EFI_BOOT_SERVICES;

// Global pointer to Boot Services (Provided by UEFI Core in real env)
extern EFI_BOOT_SERVICES *gBS;

/**
 * Main Entry Point
 */
EFI_STATUS
EFIAPI
UefiMain (
    IN EFI_HANDLE ImageHandle,
    IN EFI_SYSTEM_TABLE *SystemTable
    )
{
    EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop = NULL;
    EFI_STATUS Status;
    UINT8 *FrameBuffer;
    UINTN NumPixels;
    UINTN Index;

    // 1. Locate the GOP Protocol
    Status = gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid, NULL, (VOID **)&Gop);
    if (EFI_ERROR(Status) || Gop == NULL) {
        return Status; // Failed to find GOP
    }

    // 2. Ensure we are in a valid mode (Optional: Query and Set if needed)
    // For simplicity, we assume the current mode set by firmware is acceptable.
    // If specific resolution is needed, one would call QueryMode and SetMode here.

    // 3. Get Frame Buffer Address
    FrameBuffer = (UINT8 *)(UINTN)Gop->Mode->FrameBufferBase;
    
    // Calculate number of pixels based on current mode info
    // Note: PixelsPerScanLine might be larger than HorizontalResolution due to padding
    UINT32 Width = Gop->Mode->Info->HorizontalResolution;
    UINT32 Height = Gop->Mode->Info->VerticalResolution;
    UINT32 PixelsPerScanLine = Gop->Mode->Info->PixelsPerScanLine;
    
    // Total bytes to fill. We will fill the entire buffer provided by GOP.
    UINTN BufferSize = Gop->Mode->FrameBufferSize;

    // 4. Fill Screen with Red
    // The pixel format determines how "Red" is represented.
    // Common formats:
    // - PixelRedGreenBlueReserved8BitPerColor: R=0xFF, G=0x00, B=0x00, Res=0x00 -> 0xFF000000 (if little endian 32-bit write) or byte sequence FF 00 00 00
    // - PixelBlueGreenRedReserved8BitPerColor: B=0xFF, G=0x00, R=0x00, Res=0x00 -> Byte sequence 00 00 FF 00
    
    EFI_GRAPHICS_PIXEL_FORMAT Format = Gop->Mode->Info->PixelFormat;
    UINT32 ColorValue = 0;

    if (Format == PixelRedGreenBlueReserved8BitPerColor) {
        // Memory layout: [R][G][B][Res]
        // Little Endian 32-bit word: 0x000000FF (Res=0, B=0, G=0, R=255)
        // But when writing bytes sequentially:
        // Byte 0: R (0xFF)
        // Byte 1: G (0x00)
        // Byte 2: B (0x00)
        // Byte 3: Res (0x00)
        // So a 32-bit integer 0x000000FF written to memory becomes FF 00 00 00 on Little Endian.
        ColorValue = 0x000000FF; 
    } else if (Format == PixelBlueGreenRedReserved8BitPerColor) {
        // Memory layout: [B][G][R][Res]
        // Byte 0: B (0x00)
        // Byte 1: G (0x00)
        // Byte 2: R (0xFF)
        // Byte 3: Res (0x00)
        // 32-bit integer: 0x00FF0000
        ColorValue = 0x00FF0000;
    } else if (Format == PixelBitMask) {
        // Construct color based on masks. 
        // Simplified: Assume standard RGB masks if available, otherwise default to 0
        // For robustness, we should check masks. Here we assume Red mask is lowest bits or similar.
        // A safe bet for "Red" in bitmask is usually setting the Red bits.
        // Without specific mask values in this snippet, we'll default to a simple pattern or skip.
        // Let's assume a common mask: R=0x00FF0000, G=0x0000FF00, B=0x000000FF (common in some BIOS impls)
        // Or R=0x000000FF. 
        // To be safe and generic without complex mask parsing logic in this snippet:
        // We will just fill with 0xFF assuming 8-bit R is significant or use Blt which handles format.
        
        // However, direct memset requires knowing exact byte pattern.
        // Let's use a simple approach: Fill with 0xFF. 
        // If R is the first byte (RGBR), it works. If B is first (BGR), it makes it Blue.
        // Given the ambiguity, using Blt is safer, but prompt implies direct manipulation or simple struct usage.
        // Let's stick to the most common Little-Endian 32bpp formats.
        ColorValue = 0x000000FF; // Default assumption
    } else {
        return EFI_UNSUPPORTED;
    }

    // Optimization: Use 32-bit writes if possible, otherwise byte-by-byte
    // Since FrameBufferSize might not be divisible by 4 in some edge cases (unlikely for GOP),
    // we iterate by bytes for maximum compatibility with the "Raw" requirement.
    
    // However, iterating byte-by-byte is slow. Let's try to use 32-bit writes.
    UINT32 *FrameBuffer32 = (UINT32 *)FrameBuffer;
    UINTN NumPixels32 = BufferSize / 4;
    
    for (Index = 0; Index < NumPixels32; Index++) {
        FrameBuffer32[Index] = ColorValue;
    }
    
    // Handle remaining bytes if any
    UINT8 *RemainingBytes = (UINT8 *)&FrameBuffer32[NumPixels32];
    UINTN RemainingCount = BufferSize % 4;
    UINT8 *ColorBytes = (UINT8 *)&ColorValue;
    
    for (Index = 0; Index < RemainingCount; Index++) {
        RemainingBytes[Index] = ColorBytes[Index];
    }

    return EFI_SUCCESS;
}


评论