使用 UEFI 图形输出协议 GOP 在屏幕上显示图像的方法
。下面是我们本次的工程目录。MyPkg├── Application│ └── GopDrawApp│ ├── GopDrawApp.c│ └── GopDrawApp.inf├── MyPkg.dec└── MyPkg.dsc什么是图形输出协议GOPGOPGraphics Output Protocol图形输出协议是 UEFI 规范中的一个核心接口用来在操作系统启动前接管显卡并显示图形界面。接下来我们分两部分来介绍首先用最简单的语言来说明 GOP 控制显示的原理然后分析一下 UEFI 中 GOP 的源码定义。我们在屏幕上看到的一幅完整图片是由有限的特定像素点组成的比如常见的 1920*1080 分辨率。每个像素点通过红绿蓝三原色的不同强弱组合便能够显示特定的色彩。显存中有一块专门用于存储当前画面像素颜色信息的区域称为 FrameBuffer帧缓冲区。屏幕显示控制器会不断从 FrameBuffer 中读取数据并将其转换为屏幕上的图像。GOP 的作用就是为操作系统启动前的软件如 BootLoader、UEFI 应用提供访问 FrameBuffer 的标准接口。通过 GOP我们可以获取 FrameBuffer 的地址、分辨率、像素格式并直接向其中写入数据从而在屏幕上显示想要的图像。以下是 GOP 在 UEFI 中的 Protocol 定义。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;};QueryMode— 查询模式。用于枚举显卡所支持的所有显示模式。在设置分辨率之前你需要先调用它来查询有哪些模式可用。你需要传入一个ModeNumber从 0 开始它会返回该模式对应的具体信息分辨率、刷新率等。SetMode— 设置模式。将显示设备切换到你指定的某个模式。调用成功后帧缓冲区的分辨率、像素格式等会随之改变。你需要传入一个ModeNumber这个编号必须是通过QueryMode查询得到的有效编号。Blt— 像素块传输。这是一个高效的图形绘制函数。它可以在帧缓冲区内部、或是在帧缓冲区和内存缓冲区之间复制、填充或转换矩形像素块。Mode— 当前模式信息。这是一个只读的状态信息。它包含了当前显示模式的具体参数方便你随时查询。MaxModeQueryMode 支持的最大模式数量。Mode当前激活的模式编号。Info指向 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION 的指针包含当前模式的详细信息分辨率、像素格式等。SizeOfInfo上述 Info 结构体的大小。FrameBufferBase帧缓冲区的基地址显存中的物理地址。你向屏幕写入数据就是向这个地址开始的内存写入。FrameBufferSize帧缓冲区的大小字节。一个典型的 GOP 使用流程如下枚举调用Gop-QueryMode()获取所有支持的分辨率。设置调用Gop-SetMode()切换到你需要的分辨率比如 1920x1080。获取信息访问Gop-Mode-FrameBufferBase和Gop-Mode-Info-PixelFormat知道往哪里写数据以及以什么格式写。绘制向FrameBufferBase写入像素数据或调用Gop-Blt()来高效绘制图形。源码示例此代码大致逻辑如下首先需要获取 GOP 的 Protocol 指针。随后即可使用 GOP 的功能。使用Gop-Mode-MaxMode获取显卡支持的显示格式数量然后调用Gop-QueryMode()枚举这些格式信息。调用Gop-SetMode()设置其中一个格式。最后通过写入 FrameBuffer 来显示一幅图像示例。GopDrawApp.c 源码如下#include Uefi.h#include Library/UefiLib.h#include Library/UefiApplicationEntryPoint.h#include Library/UefiBootServicesTableLib.h#include Protocol/GraphicsOutput.h#define DRAWEFI_STATUSEFIAPIUefiMain (IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable){EFI_STATUS Status;EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;UINT32 MaxMode;UINT32 ModeNumber;UINTN SizeOfInfo;UINT32 SelectedMode 0;UINT32 Horizontal 0;UINT32 Vertical 0;UINTN Index;EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *ModeInfo NULL;// 1. 获取 GOP ProtocolStatus gBS-LocateProtocol(gEfiGraphicsOutputProtocolGuid,NULL,(VOID**)Gop);if (EFI_ERROR(Status)) {Print(LFailed to locate GOP\n);return Status;}Print(LGOP protocol found successfully!\n);// 2. 枚举所有显示模式Print(L Enumerating All Display Modes \n);Print(LMaxMode %d\n\n, Gop-Mode-MaxMode); // 支持的显示模式个数MaxMode Gop-Mode-MaxMode;for (ModeNumber 0; ModeNumber MaxMode; ModeNumber) {Status Gop-QueryMode(Gop, ModeNumber, SizeOfInfo, ModeInfo);if (EFI_ERROR(Status)) {Print(LQueryMode failed for mode %d: %r\n, ModeNumber, Status);continue;}Print(LMode %2d: %4d x %4d, PixelFormat %d\n,ModeNumber,ModeInfo-HorizontalResolution,ModeInfo-VerticalResolution,ModeInfo-PixelFormat);// 注意ModeInfo 是由 GOP 内部分配的内存不需要我们手动释放}// 3. 设置显示模式Status Gop-SetMode(Gop, SelectedMode);if (EFI_ERROR(Status)) {Print(LFailed to set mode %d: %r\n, SelectedMode, Status);return Status;}Horizontal Gop-Mode-Info-HorizontalResolution;Vertical Gop-Mode-Info-VerticalResolution;Print(LSelected mode %d with resolution %d x %d\n, SelectedMode, Horizontal, Vertical);Print(LMode %d set successfully!\n, SelectedMode);// 3. 当前显示模式信息Print(L\n Current Mode Information \n);Print(LCurrent Mode Number: %d\n, Gop-Mode-Mode);Print(LFrameBuffer Base: 0x%016lx\n, Gop-Mode-FrameBufferBase);Print(LFrameBuffer Size: %d bytes (%.2f MB)\n,Gop-Mode-FrameBufferSize,(double)Gop-Mode-FrameBufferSize / (1024 * 1024));Print(LHorizontal Resolution: %d\n, Gop-Mode-Info-HorizontalResolution);Print(LVertical Resolution: %d\n, Gop-Mode-Info-VerticalResolution);Print(LPixel Format: %d\n, Gop-Mode-Info-PixelFormat);// 解释像素格式switch (Gop-Mode-Info-PixelFormat) {case PixelRedGreenBlueReserved8BitPerColor:Print(L - Pixel Format: RGB (8:8:8, with reserved byte)\n);break;case PixelBlueGreenRedReserved8BitPerColor:Print(L - Pixel Format: BGR (most common on PC)\n);break;case PixelBitMask:Print(L - Pixel Format: Custom bitmask\n);break;case PixelBltOnly:Print(L - Pixel Format: Blt only (no direct framebuffer access)\n);break;default:Print(L - Pixel Format: Unknown\n);}Print(L\nPress any key to exit...\n);SystemTable-ConIn-Reset(SystemTable-ConIn, FALSE);// 让程序暂停等待用户按下一个键// 要等待的事件数量等待什么事件输出参数哪个事件被触发。SystemTable-BootServices-WaitForEvent(1, SystemTable-ConIn-WaitForKey, Index);#ifdef DRAW// 4. 帧缓冲地址直接写像素UINT32 *FrameBuffer (UINT32*)Gop-Mode-FrameBufferBase;UINT32 PixelsPerScanLine Gop-Mode-Info-PixelsPerScanLine;UINT32 x, y;// 5. 画渐变背景for (y 0; y Vertical; y) {for (x 0; x Horizontal; x) {UINT8 r (UINT8)(x * 255 / Horizontal);UINT8 g (UINT8)(y * 255 / Vertical);UINT8 b 0x80;UINT32 color (r 16) | (g 8) | b;FrameBuffer[y * PixelsPerScanLine x] color;}}// 5. 画中心十字UINT32 cx Horizontal / 2;UINT32 cy Vertical / 2;for (x 0; x Horizontal; x) {FrameBuffer[cy * PixelsPerScanLine x] 0xFFFFFF; // 横线白}for (y 0; y Vertical; y) {FrameBuffer[y * PixelsPerScanLine cx] 0xFFFFFF; // 竖线}#endif // DRAWwhile(1);return EFI_SUCCESS;}显卡支持的显示格式程序显示效果如下附INF 文件[Defines]INF_VERSION 0x00010006BASE_NAME GopDrawAppFILE_GUID 4e397097-665f-4745-88c3-6305ac8623aaMODULE_TYPE UEFI_APPLICATIONVERSION_STRING 1.0ENTRY_POINT UefiMain[Sources]GopDrawApp.c[Packages]MdePkg/MdePkg.dec[LibraryClasses]UefiApplicationEntryPointUefiLibUefiBootServicesTableLibDEC 文件[Defines]DEC_SPECIFICATION 0x00010005PACKAGE_NAME MyPkgPACKAGE_GUID a2ab400d-c171-43ec-a0cc-582527a93887PACKAGE_VERSION 1.0DSC 文件[Defines]PLATFORM_NAME MyPkgPLATFORM_GUID 87654321-4321-4321-4321-CBA987654321PLATFORM_VERSION 1.0DSC_SPECIFICATION 0x00010005OUTPUT_DIRECTORY Build/MyPkgSUPPORTED_ARCHITECTURES X64BUILD_TARGETS DEBUG|RELEASE[LibraryClasses]UefiLib|MdePkg/Library/UefiLib/UefiLib.infUefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.infPrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.infPcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.infMemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.infDebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.infBaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.infBaseLib|MdePkg/Library/BaseLib/BaseLib.infUefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.infDevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.infUefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.infRegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.infDebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf[Components]MyPkg/Application/GopDrawApp/GopDrawApp.inf

相关新闻

只有程序员才能读懂的西游记

只有程序员才能读懂的西游记

我佛造经传极乐 话说我佛如来为度化天下苍生,有三藏真经,可劝人为善。 就如图中所示,真经所藏之处,在于云端。佛祖所管辖之下,有四个区域Region,称为四大部洲, 一是东胜神洲,二是南…

2026/7/3 2:33:50阅读更多 →
SpringBoot日志系统

SpringBoot日志系统

前言:日志是后端项目的“黑匣子”,是排查Bug、追踪业务、统计线上问题、复盘故障的核心依据。SpringBoot 对 Java 混乱的日志体系做了统一封装,开箱即用、无需复杂配置。一、日志的核心概念简单来说,日志就是程序运行的“运行记录…

2026/7/3 2:33:50阅读更多 →
JMeter高并发压测端口耗尽问题:从原理到实战解决方案

JMeter高并发压测端口耗尽问题:从原理到实战解决方案

1. 项目概述:当压测遭遇“端口耗尽”的困境如果你正在用JMeter做压力测试,特别是并发量稍微上去一点,比如几百上千个线程,突然在“察看结果树”里看到一片刺眼的红色,错误信息里赫然写着java.net.BindException: Addre…

2026/7/3 2:33:50阅读更多 →
php路由 if路由

php路由 if路由

状态码 | 含义 | 在你路由中的作用 | AEO 影响 | | :--- | :--- | :--- | :--- | | 404 | Not Found(未找到) | 用户访问了路由表中未注册的路径时触发 | ✅ 告诉 AI “此页面不存在”,防止收录无效链接 | | 500 | Internal Server Error&…

2026/7/3 3:48:55阅读更多 →
Linux CPU瓶颈排查神器!mpstat命令超全详解|多核CPU精准监控

Linux CPU瓶颈排查神器!mpstat命令超全详解|多核CPU精准监控

1. 命令简介mpstat 命令是 sysstat 工具集的一部分,用于实时监控多处理器(多核CPU)系统中各个CPU核心的利用率统计信息。它能够清晰地展示用户态、内核态、I/O等待、中断处理等不同维度的CPU时间分配情况,是进行系统性能分析、定位…

2026/7/3 3:48:55阅读更多 →
【学习记录】Week9(一):glibc堆结构精读与堆风水方法论——堆利用的基石

【学习记录】Week9(一):glibc堆结构精读与堆风水方法论——堆利用的基石

写在前面:在Week8中,我们探讨了整数漏洞如何导致堆溢出。本周,我们将深入glibc堆管理的核心机制,并学习堆风水这一高级技术。堆风水是所有堆漏洞利用稳定性的前提,也是CTF比赛中堆题的必杀技。今天,我们先从…

2026/7/3 3:48:55阅读更多 →
【学习记录】Week8(四):从整数漏洞到堆溢出——实战利用与完整EXP构造

【学习记录】Week8(四):从整数漏洞到堆溢出——实战利用与完整EXP构造

写在前面:在Week8的前三篇中,我们系统学习了整数溢出/下溢、符号转换与长度计算错误的原理,并探讨了它们如何导致堆溢出。今天,我们将迎来本周的收官之战——从理论走向实践,通过完整的实战案例,手把手教你…

2026/7/3 3:48:55阅读更多 →
谷歌GEO:AI搜索时代,大鱼营销助力出海企业解锁新流量赛道

谷歌GEO:AI搜索时代,大鱼营销助力出海企业解锁新流量赛道

生成式AI的普及,正在重塑海外用户的信息获取与品牌认知路径。以往海外采购、消费用户依赖谷歌传统搜索浏览网页,如今更多人习惯通过Google AI Overview、Gemini、ChatGPT等工具直接提问,依托AI整合的完整答案完成品牌筛选与决策。这一行业变化…

2026/7/3 3:48:55阅读更多 →
大模型轻量化推理技术选型与实践指南

大模型轻量化推理技术选型与实践指南

我不能按照该标题生成内容。原因如下:标题中涉及对特定企业家(李彦宏)的主观定性表述(如“闭源‘遗志’”),属于未经证实的价值判断,隐含贬义与戏谑色彩,不符合内容安全规范中“不得…

2026/7/3 3:43:55阅读更多 →
AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

6个月前的2025年12月,Boris Cherny 公开宣布自己卸载了 IDE。一时间,Vibe Coding 成了全行业最热的话题。6个月后,当我们回过头来拉一份真实账本,发现事情远没有"一句话生成一个App"那么浪漫。本文从产品经理和研发两个…

2026/7/2 12:10:34阅读更多 →
审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

引言:审计结束三个月了,审计员的权限还没关某城商行每年按照监管要求开展至少一次数据安全审计。审计期间,内审部门需要抽样检查各类业务数据——交易流水、客户信息、员工操作日志、权限配置记录。这些数据分布在不同系统中,审计…

2026/7/2 12:10:34阅读更多 →
LV3296与PIC18F45K22的UART通信与USB扩展方案

LV3296与PIC18F45K22的UART通信与USB扩展方案

1. LV3296与PIC18F45K22的硬件搭档解析在嵌入式数据采集系统中,LV3296条形码扫描模块与PIC18F45K22微控制器的组合堪称经典搭配。LV3296作为一款工业级条码扫描头,其核心是一颗高性能CMOS图像传感器,配合专用解码芯片,能自动识别包…

2026/7/3 0:03:41阅读更多 →
AI初创生存指南:6个月完成可信度验证闭环

AI初创生存指南:6个月完成可信度验证闭环

1. 这不是“逆袭指南”,而是一份AI初创公司真实生存手记“How To Beat Odds As an AI Startup?”——这个标题乍看像一句热血口号,但在我带过7个从0到1的AI产品团队、亲手踩过融资失败、技术债崩盘、客户POC卡在最后一公里等23类典型坑之后,…

2026/7/3 0:03:41阅读更多 →
多模态+推理链+RAG 2.0+智能体:工业级AI系统落地四支柱

多模态+推理链+RAG 2.0+智能体:工业级AI系统落地四支柱

1. 这不是又一篇“AI趋势速览”,而是一份实操者手记:当多模态、推理链、检索增强与智能体协作真正撞进工程现场“LAI #73”这个编号本身就像一个暗号——它不属于某家大厂的白皮书,也不是学术会议的议程表,而是长期泡在模型训练集…

2026/7/3 0:03:41阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/3 1:12:46阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/3 1:36:36阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/3 2:08:15阅读更多 →