ReactOS 图形系统分析(1):VBE MiniPort 驱动 vgapnp.sys

ReactOS 图形系统分析(1):VBE MiniPort 驱动 vgapnp.sys

1. 概述

vgapnp.sys 是 ReactOS 的 VBE(VESA BIOS Extensions)MiniPort 视频驱动 ,源代码位于 win32ss/drivers/miniport/vbe/,模块名为 vgapnp,由以下文件构成:

文件 功能
vbemp.c(file:///d:/reactos/win32ss/drivers/miniport/vbe/vbemp.c) 主实现:驱动入口、硬件检测、模式管理、IOCTL 处理
vbemp.h(file:///d:/reactos/win32ss/drivers/miniport/vbe/vbemp.h) 头文件:VBE 规范定义、数据结构、函数声明
edid.c(file:///d:/reactos/win32ss/drivers/miniport/vbe/edid.c) EDID 显示器信息读取
vbemp_reg.inf(file:///d:/reactos/win32ss/drivers/miniport/vbe/vbemp_reg.inf) 注册表 INF:注册服务与设备
vbemp.rc(file:///d:/reactos/win32ss/drivers/miniport/vbe/vbemp.rc) 版本资源

作用: 该驱动通过 VBE 标准接口(INT 10 中断 + 实模式内存通信)驱动 VBE 兼容显卡(包括 VirtualBox 虚拟显卡),实现视频模式枚举、切换、帧缓冲内存映射、调色板设置和电源管理。

在上一份分析(VirtualBox虚拟机显示驱动分析.md)中已确认:VirtualBox 虚拟机中该驱动(vgapnp.sys)被实际加载,与 videoprt.sys(视频端口驱动)、framebuf.dll(显示驱动 DDI)构成完整显示栈。


2. 驱动模型与架构定位

2.1 Windows 视频驱动分层

复制代码
┌────────────────────────────────────────────┐
│ win32k.sys (内核 GDI + 显示驱动 DDI 加载)   │
│         │ 通过 EngpRegisterGraphicsDevice  │
├─────────┼──────────────────────────────────┤
│ 显示驱动 DDI (framebuf.dll)               │
│   - 实现 DrvEnablePDEV/DrvEnableSurface   │
│   - 调用 EngDeviceIoControl 下发 IOCTL     │
├─────────┼──────────────────────────────────┤
│ 视频端口驱动 (videoprt.sys)                │
│   - 视频端口驱动层 (VideoPort* 函数)       │
│   - 管理 MiniPort 驱动                     │
│   - 提供 INT 10 实模式 BIOS 调用服务       │
├─────────┼──────────────────────────────────┤
│ MiniPort 驱动 (vgapnp.sys) ← 本分析对象    │
│   - 实现 HwFindAdapter/HwInitialize 等     │
│   - 通过 VIDEO_PORT_INT10_INTERFACE 调用   │
│   - 通过 IOCTL 响应显示驱动请求             │
├─────────┼──────────────────────────────────┤
│ VBE BIOS (显卡 ROM)                        │
│   - INT 10 中断服务                       │
│   - 实模式内存通信 (V86 模式)              │
└────────────────────────────────────────────┘

2.2 MiniPort 驱动基本模型

MiniPort 驱动(如 vgapnp.sys)不直接与硬件交互,而是通过 VideoPort 服务函数 *(由 videoprt.sys 提供)操作硬件。驱动初始化时通过 VIDEO_HW_INITIALIZATION_DATA 结构向端口驱动注册一组回调:

c 复制代码
VIDEO_HW_INITIALIZATION_DATA InitData;
VideoPortZeroMemory(&InitData, sizeof(InitData));
InitData.HwInitDataSize = sizeof(VIDEO_HW_INITIALIZATION_DATA);
InitData.HwFindAdapter = VBEFindAdapter;      // 检测适配器
InitData.HwInitialize = VBEInitialize;        // 初始化
InitData.HwStartIO = VBEStartIO;              // IOCTL 分发
InitData.HwResetHw = VBEResetHw;              // 硬件复位
InitData.HwGetPowerState = VBEGetPowerState;  // 电源查询
InitData.HwSetPowerState = VBESetPowerState;  // 电源设置
InitData.HwGetVideoChildDescriptor = VBEGetVideoChildDescriptor; // 子设备(显示器)枚举
InitData.HwDeviceExtensionSize = sizeof(VBE_DEVICE_EXTENSION);   // 设备扩展大小
InitData.HwLegacyResourceList = VBEAccessRange; // 硬件资源范围
InitData.HwLegacyResourceCount = ARRAYSIZE(VBEAccessRange);

return VideoPortInitialize(Context1, Context2, &InitData, NULL);

3. 核心数据结构

3.1 VBE_DEVICE_EXTENSION --- 设备扩展

设备扩展是驱动为每个适配器实例保存状态的私有结构:

c 复制代码
typedef struct
{
   /* 与 videoprt 的 Int10 调用接口 */
   VIDEO_PORT_INT10_INTERFACE Int10Interface;

   /* 与 VBE 实模式接口通信的跳板内存 */
   USHORT TrampolineMemorySegment;   // 段地址 (实模式)
   USHORT TrampolineMemoryOffset;    // 偏移地址

   /* 通用控制器/BIOS 信息 */
   VBE_INFO VbeInfo;

   /* 保存的视频模式信息 */
   ULONG ModeCount;       // 支持的显示模式数量
   USHORT *ModeNumbers;   // VBE 模式编号数组
   PVBE_MODEINFO ModeInfo; // 模式详细信息数组
   USHORT CurrentMode;    // 当前模式索引

   /* 当前正在枚举的子设备 */
   ULONG CurrentChildIndex;
} VBE_DEVICE_EXTENSION, *PVBE_DEVICE_EXTENSION;

3.2 VBE_INFO --- VBE 控制器信息(VBE 规范结构)

VBE_GET_CONTROLLER_INFORMATION(0x4F00)调用返回:

c 复制代码
typedef struct
{
   CHAR Signature[4];        // "VESA" 签名
   USHORT Version;           // 版本号 (0x200 = VBE 2.0, 0x300 = VBE 3.0)
   ULONG OemStringPtr;       // OEM 字符串指针 (实模式地址)
   LONG Capabilities;        // 能力标志
   ULONG VideoModePtr;       // 视频模式列表指针 (实模式地址)
   USHORT TotalMemory;       // 显存总量 (单位 64KB)
   USHORT OemSoftwareRevision;
   ULONG OemVendorNamePtr;   // 厂商名指针
   ULONG OemProductNamePtr;  // 产品名指针
   ULONG OemProductRevPtr;   // 产品版本指针
   CHAR Reserved[222];
   CHAR OemData[256];
} VBE_INFO, *PVBE_INFO;

3.3 VBE_MODEINFO --- 视频模式信息(VBE 规范结构)

VBE_GET_MODE_INFORMATION(0x4F01)调用返回,驱动用它筛选可用模式:

c 复制代码
typedef struct
{
   /* 所有 VBE 版本必需 */
   USHORT ModeAttributes;      // bit7 = 支持线性帧缓冲
   UCHAR WinAAttributes;
   UCHAR WinBAttributes;
   USHORT WinGranularity;
   USHORT WinSize;
   USHORT WinASegment;
   USHORT WinBSegment;
   ULONG WinFuncPtr;
   USHORT BytesPerScanLine;    // 每扫描行字节数 (窗口模式)

   /* VBE 1.2+ */
   USHORT XResolution;         // 水平分辨率
   USHORT YResolution;         // 垂直分辨率
   UCHAR XCharSize;
   UCHAR YCharSize;
   UCHAR NumberOfPlanes;       // 位面数
   UCHAR BitsPerPixel;         // 每像素位数
   UCHAR NumberOfBanks;
   UCHAR MemoryModel;          // 内存模型 (4=打包像素, 6=直接色)
   UCHAR BankSize;
   UCHAR NumberOfImagePages;
   UCHAR Reserved1;

   /* Direct Color 字段 */
   UCHAR RedMaskSize;   UCHAR RedFieldPosition;
   UCHAR GreenMaskSize; UCHAR GreenFieldPosition;
   UCHAR BlueMaskSize;  UCHAR BlueFieldPosition;
   UCHAR ReservedMaskSize; UCHAR ReservedFieldPosition;
   UCHAR DirectColorModeInfo;

   /* VBE 2.0+ */
   ULONG PhysBasePtr;          // 线性帧缓冲物理地址 ← 关键!
   ULONG Reserved2;
   USHORT Reserved3;

   /* VBE 3.0+ */
   USHORT LinBytesPerScanLine; // 线性模式每行字节数
   UCHAR BnkNumberOfImagePages;
   UCHAR LinNumberOfImagePages;
   UCHAR LinRedMaskSize;  UCHAR LinRedFieldPosition;
   UCHAR LinGreenMaskSize;UCHAR LinGreenFieldPosition;
   UCHAR LinBlueMaskSize; UCHAR LinBlueFieldPosition;
   UCHAR LinReservedMaskSize; UCHAR LinReservedFieldPosition;
   ULONG MaxPixelClock;
   CHAR Reserved4[189];
} VBE_MODEINFO, *PVBE_MODEINFO;

3.4 VBEAccessRange --- 硬件资源范围

c 复制代码
VIDEO_ACCESS_RANGE VBEAccessRange[] =
{
    { {{0x3b0}}, 0x3bb - 0x3b0 + 1, 1, 1, 0 },  // 视频 I/O 端口 0x3b0-0x3bb (MDA/Hercules)
    { {{0x3c0}}, 0x3df - 0x3c0 + 1, 1, 1, 0 },  // 视频 I/O 端口 0x3c0-0x3df (VGA)
    { {{0xa0000}}, 0x20000, 0, 1, 0 },           // VGA 显存 0xA0000-0xBFFFF (128KB)
};

4. 主要功能模块分析

4.1 适配器检测 --- VBEFindAdapter

c 复制代码
VP_STATUS NTAPI
VBEFindAdapter(IN PVOID HwDeviceExtension, IN PVOID HwContext,
               IN PWSTR ArgumentString, IN OUT PVIDEO_PORT_CONFIG_INFO ConfigInfo,
               OUT PUCHAR Again)
{
   if (VideoPortIsNoVesa())        // 如果 boot 时明确禁用 VESA
       return ERROR_DEV_NOT_EXIST;

   if (ConfigInfo->Length < sizeof(VIDEO_PORT_CONFIG_INFO))
       return ERROR_INVALID_PARAMETER;

   ConfigInfo->VdmPhysicalVideoMemoryAddress = VBEAccessRange[2].RangeStart; // 0xA0000
   ConfigInfo->VdmPhysicalVideoMemoryLength = VBEAccessRange[2].RangeLength;  // 0x20000
   return NO_ERROR;
}

设计要点:

  • 初始化早期无法使用 INT 10 服务检测硬件,因此总是返回 NO_ERROR(假定有 VBE),真正的检测延迟到 VBEInitialize
  • 通过 VideoPortIsNoVesa() 支持启动参数禁用(/NoVesa 或注册表)
  • 向端口驱动报告 VDM(VGA 兼容内存)物理地址为 0xA0000,长度 0x20000

4.2 驱动初始化 --- VBEInitialize

这是核心函数,完成以下步骤:

VBEInitialize 初始化流程图:
渲染错误: Mermaid 渲染失败: Parse error on line 12: ... H{版本 >= 0x200?
(VBE 2.0+ 线性帧缓冲)} -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PS'

① 获取 INT 10 接口:

c 复制代码
VBEDeviceExtension->Int10Interface.Version = VIDEO_PORT_INT10_INTERFACE_VERSION_1;
VBEDeviceExtension->Int10Interface.Size = sizeof(VIDEO_PORT_INT10_INTERFACE);
Status = VideoPortQueryServices(HwDeviceExtension, VideoPortServicesInt10,
                                (PINTERFACE)&VBEDeviceExtension->Int10Interface);

VIDEO_PORT_INT10_INTERFACE 由 videoprt.sys 提供,包含:

  • Int10CallBios --- 调用实模式 BIOS 中断
  • Int10AllocateBuffer --- 在第一个 1MB 内分配实模式内存
  • Int10WriteMemory / Int10ReadMemory --- 读写实模式内存
  • Int10FreeBuffer --- 释放缓冲区

② 分配跳板内存(Trampoline Memory):

c 复制代码
Length = 0x400;
Status = Int10Interface.Int10AllocateBuffer(Context,
   &VBEDeviceExtension->TrampolineMemorySegment,   // 段地址
   &VBEDeviceExtension->TrampolineMemoryOffset,    // 偏移
   &Length);

该内存在物理内存第一个 1MB 内,必须能被 V86 模式(实模式)访问。

③ 获取 VBE 控制器信息:

c 复制代码
/* 在跳板内存写入 "VBE2" 签名(要求返回 VBE 2.0+ 信息) */
Int10WriteMemory(Context, Seg, Off, "VBE2", 4);

VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
BiosRegisters.Eax = VBE_GET_CONTROLLER_INFORMATION; // 0x4F00
BiosRegisters.Edi = TrampolineMemoryOffset;         // ES:DI → 缓冲区
BiosRegisters.SegEs = TrampolineMemorySegment;
Int10CallBios(Context, &BiosRegisters);

/* 验证返回码和 "VESA" 签名 */
if (VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_SUCCESS)  // 低16位 == 0x004F
{
   Int10ReadMemory(..., &VBEDeviceExtension->VbeInfo, sizeof(VBE_INFO));
   if (VideoPortCompareMemory(VbeInfo.Signature, "VESA", 4) != 4)
       return FALSE;
}

④ 版本检查:

c 复制代码
#ifdef VBE12_SUPPORT
   if (Version < 0x102) return FALSE;   // 需要 VBE 1.2+
#else
   if (Version < 0x200) return FALSE;   // 默认需要 VBE 2.0+ (线性帧缓冲)
#endif

⑤ 枚举视频模式列表:

c 复制代码
/* 模式列表存储于 VBE BIOS 内存(VbeInfo.VideoModePtr 指向实模式地址)*/
for (ModeCount = 0; ; ModeCount++)
{
   Int10ReadMemory(Context,
      HIWORD(VbeInfo.VideoModePtr),                       // 段
      LOWORD(VbeInfo.VideoModePtr) + (ModeCount << 1),    // 偏移 + 2字节/项
      &ModeTemp, sizeof(ModeTemp));
   if (ModeTemp == 0xFFFF || ModeTemp == 0)   // 列表以 0xFFFF 或 0 结尾
      break;
}

⑥ 逐模式查询并筛选:

c 复制代码
for (CurrentMode = 0, SuitableModeCount = 0; CurrentMode < ModeCount; CurrentMode++)
{
   /* 读取模式编号 */
   Int10ReadMemory(..., &ModeTemp, sizeof(ModeTemp));

   /* 调用 VBE BIOS 查询模式信息 */
   BiosRegisters.Eax = VBE_GET_MODE_INFORMATION;  // 0x4F01
   BiosRegisters.Ecx = ModeTemp;                  // CX = 模式号
   BiosRegisters.Edi = TrampolineMemoryOffset + 0x200;  // 缓冲区
   BiosRegisters.SegEs = TrampolineMemorySegment;
   Int10CallBios(Context, &BiosRegisters);
   Int10ReadMemory(..., ModeInfo + SuitableModeCount, sizeof(VBE_MODEINFO));

   /* 筛选条件 */
   if (返回成功 &&
       XResolution >= 640 && YResolution >= 480 &&      // 至少 640x480
       (MemoryModel == PACKEDPIXEL || DIRECTCOLOR) &&    // 打包像素或直接色
       PhysBasePtr != 0 &&                               // 有线性帧缓冲地址
       (ModeAttributes & VBE_MODEATTR_LINEAR))           // 支持线性模式
   {
      ModeNumbers[SuitableModeCount] = ModeTemp | 0x4000;  // 设置线性帧缓冲标志位
      SuitableModeCount++;
   }
}

关键点: ModeTemp | 0x4000 --- VBE 规范中模式号最高位(bit 14)表示使用线性帧缓冲(LFB)模式,这是 32 位驱动的首选。

⑦ 排序模式列表: 使用冒泡排序(VBESortModes),优先按位深(BitsPerPixel),再按分辨率排序。这样 ModeIndex 0 通常是最小分辨率/最低位深,便于 framebuf 选择。

⑧ 枚举子设备: VideoPortEnumerateChildren 触发 VBEGetVideoChildDescriptor 回调(见 EDID 部分)。

4.3 IOCTL 分发 --- VBEStartIO

显示驱动(framebuf.dll)通过 EngDeviceIoControl 下发 IOCTL,VBEStartIO 是统一入口:

IOCTL 处理函数 功能
IOCTL_VIDEO_SET_CURRENT_MODE VBESetCurrentMode 设置显示模式
IOCTL_VIDEO_RESET_DEVICE VBEResetDevice 复位到默认模式
IOCTL_VIDEO_MAP_VIDEO_MEMORY VBEMapVideoMemory 映射帧缓冲到虚拟地址
IOCTL_VIDEO_UNMAP_VIDEO_MEMORY VBEUnmapVideoMemory 解除帧缓冲映射
IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES VBEQueryNumAvailModes 查询可用模式数量
IOCTL_VIDEO_QUERY_AVAIL_MODES VBEQueryAvailModes 查询所有可用模式
IOCTL_VIDEO_QUERY_CURRENT_MODE VBEQueryCurrentMode 查询当前模式
IOCTL_VIDEO_SET_COLOR_REGISTERS VBESetColorRegisters 设置调色板 (CLUT)

分发模式: 每个 case 先检查缓冲区长度,不足则返回 ERROR_INSUFFICIENT_BUFFER;成功统一设置 StatusBlock->Status = NO_ERROR

IOCTL 请求处理数据流图:
#mermaid-svg-mdEwxyyH8Ht57Qd5{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mdEwxyyH8Ht57Qd5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .error-icon{fill:#552222;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .marker.cross{stroke:#333333;}#mermaid-svg-mdEwxyyH8Ht57Qd5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mdEwxyyH8Ht57Qd5 p{margin:0;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .cluster-label text{fill:#333;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .cluster-label span{color:#333;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .cluster-label span p{background-color:transparent;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .label text,#mermaid-svg-mdEwxyyH8Ht57Qd5 span{fill:#333;color:#333;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .node rect,#mermaid-svg-mdEwxyyH8Ht57Qd5 .node circle,#mermaid-svg-mdEwxyyH8Ht57Qd5 .node ellipse,#mermaid-svg-mdEwxyyH8Ht57Qd5 .node polygon,#mermaid-svg-mdEwxyyH8Ht57Qd5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .rough-node .label text,#mermaid-svg-mdEwxyyH8Ht57Qd5 .node .label text,#mermaid-svg-mdEwxyyH8Ht57Qd5 .image-shape .label,#mermaid-svg-mdEwxyyH8Ht57Qd5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .rough-node .label,#mermaid-svg-mdEwxyyH8Ht57Qd5 .node .label,#mermaid-svg-mdEwxyyH8Ht57Qd5 .image-shape .label,#mermaid-svg-mdEwxyyH8Ht57Qd5 .icon-shape .label{text-align:center;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .node.clickable{cursor:pointer;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .arrowheadPath{fill:#333333;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mdEwxyyH8Ht57Qd5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mdEwxyyH8Ht57Qd5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mdEwxyyH8Ht57Qd5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .cluster text{fill:#333;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .cluster span{color:#333;}#mermaid-svg-mdEwxyyH8Ht57Qd5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mdEwxyyH8Ht57Qd5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .icon-shape,#mermaid-svg-mdEwxyyH8Ht57Qd5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .icon-shape p,#mermaid-svg-mdEwxyyH8Ht57Qd5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .icon-shape .label rect,#mermaid-svg-mdEwxyyH8Ht57Qd5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mdEwxyyH8Ht57Qd5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mdEwxyyH8Ht57Qd5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mdEwxyyH8Ht57Qd5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 硬件层
MiniPort 驱动 vgapnp.sys
视频端口驱动 videoprt.sys
用户模式 / 内核模式
IOCTL 请求包 (VRP)
RequestPacket
SET_CURRENT_MODE
MAP_VIDEO_MEMORY
QUERY_AVAIL_MODES
SET_COLOR_REGISTERS
其他
Int10CallBios 0x4F02
Int10CallBios 0x4F01
0x4F09 或直接端口
VideoPortMapMemory
返回状态
映射地址
framebuf.dll 显示驱动 DDI

(DrvEnablePDEV / DrvSetMode)
EngDeviceIoControl

→ 构造 IRP 下发
VideoPortStartIo

→ 分发到 HwStartIO
VBEStartIO (HwStartIO)
switch (IoControlCode)
VBESetCurrentMode
VBEMapVideoMemory
VBEQueryAvailModes
VBESetColorRegisters
...其他处理函数
VBE BIOS

(INT 10 实模式调用)
帧缓冲物理内存

VideoPortMapMemory
VGA DAC 端口

0x3C8/0x3C9

4.4 模式设置 --- VBESetCurrentMode

c 复制代码
BOOLEAN FASTCALL VBESetCurrentMode(DeviceExtension, RequestedMode, StatusBlock)
{
   if (RequestedMode->RequestedMode >= DeviceExtension->ModeCount)
      return ERROR_INVALID_PARAMETER;

   BiosRegisters.Eax = VBE_SET_VBE_MODE;  // 0x4F02
   BiosRegisters.Ebx = ModeNumbers[RequestedMode->RequestedMode]; // BX = 模式号
   Int10CallBios(Context, &BiosRegisters);

   if (VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_SUCCESS)
      DeviceExtension->CurrentMode = RequestedMode->RequestedMode;  // 记录当前模式
   else
      DeviceExtension->CurrentMode = -1;
   ...
}

4.5 帧缓冲内存映射 --- VBEMapVideoMemory

这是 framebuf 显示驱动工作的基础。framebuf 需要获得线性帧缓冲的物理地址并将其映射到内核虚拟地址:

c 复制代码
BOOLEAN FASTCALL VBEMapVideoMemory(DeviceExtension, RequestedAddress, MapInformation, StatusBlock)
{
   if (ModeInfo[CurrentMode].ModeAttributes & VBE_MODEATTR_LINEAR)
   {
      /* 线性帧缓冲模式:物理地址直接来自 VBE 模式信息 */
      FrameBuffer.QuadPart = ModeInfo[CurrentMode].PhysBasePtr;

      /* 帧缓冲长度 = 每行字节数 × 行数 */
      if (VbeInfo.Version < 0x300)
         VideoRamLength = ModeInfo[CurrentMode].BytesPerScanLine * YResolution;
      else
         VideoRamLength = ModeInfo[CurrentMode].LinBytesPerScanLine * YResolution;  // VBE 3.0+
   }
#ifdef VBE12_SUPPORT
   else  /* 窗口模式 (非线性) */
   {
      FrameBuffer.QuadPart = 0xA0000;     // VGA 窗口
      VideoRamLength = 0x10000;           // 64KB
   }
#endif

   /* 通过端口驱动将物理内存映射到请求者的虚拟地址空间 */
   VideoPortMapMemory(DeviceExtension, FrameBuffer,
      &MapInformation->VideoRamLength, &inIoSpace,  // inIoSpace = MEMORY
      &MapInformation->VideoRamBase);
   ...
}

注意: VBE12_SUPPORT 默认未定义,因此只支持线性帧缓冲模式。

4.6 调色板设置 --- VBESetColorRegisters

两条代码路径:

① VGA 兼容控制器直接写端口(VBE Capabilities 未设置 bit 1):

c 复制代码
/* 通过 VGA DAC 端口直接编程调色板 */
for (Entry = FirstEntry; Entry < NumEntries + FirstEntry; Entry++)
{
   VideoPortWritePortUchar((PUCHAR)0x03c8, Entry);       // DAC 写索引
   VideoPortWritePortUchar((PUCHAR)0x03c9, Red);          // 红色
   VideoPortWritePortUchar((PUCHAR)0x03c9, Green);        // 绿色
   VideoPortWritePortUchar((PUCHAR)0x03c9, Blue);         // 蓝色
}

② 通过 VBE BIOS 设置调色板VBE_SET_GET_PALETTE_DATA,0x4F09):需要将 RGB 交换(VBE 存储顺序为 R-G-B 而 CLUT 接口为 B-G-R),通过跳板内存传给 BIOS。

4.7 电源管理 --- VBEGetPowerState / VBESetPowerState

通过 VBE_POWER_MANAGEMENT_EXTENSIONS(0x4F10)调用 VBE/PM 扩展:

  • EBX=0 查询电源管理支持
  • EBX=2 获取当前电源状态(DPMSVersion + PowerState)
  • EBX=1 设置电源状态(StandBy=0x100, Suspend=0x200, Off=0x400)

4.8 硬件复位 --- VBEResetHw

c 复制代码
BOOLEAN NTAPI VBEResetHw(DeviceExtension, Columns, Rows)
{
   return FALSE;  /* 返回 FALSE 让 HAL 用 INT10 复位显示 */
}

4.9 EDID 显示器信息读取 --- edid.c

VBEGetVideoChildDescriptorVideoPortEnumerateChildren 的回调,用于枚举显示器的子设备(显示器本身),读取 EDID 数据:

复制代码
ChildIndex == 0 → 不支持 ACPI 子设备枚举
ChildIndex == 1 → 显示器 (Monitor),UId=1
   ├── VBEReadEdid()           --- 直接通过 VBE_DDC (0x4F15) + VBE_DDC_READ_EDID (0x01) 读取
   └── VBEReadEdidUsingSCI()   --- 通过 I2C 位敲打 (SCL/SDA) + VideoPortDDCMonitorHelper 读取

VBE DDC 子功能:

  • VBE_DDC_READ_EDID (0x01) --- 直接读取 EDID
  • VBE_DDC_REPORT_CAPABILITIES (0x10) --- 查询 DDC 能力
  • VBE_DDC_BEGIN_SCL_SDA_CONTROL (0x11) / END (0x12) --- 开始/结束 I2C 控制
  • VBE_DDC_WRITE_SCL_CLOCK_LINE (0x13) / WRITE_SDA_DATA_LINE (0x14) / READ_SCL (0x15) / READ_SDA (0x16) --- I2C 位操作

5. VBE INT 10 调用机制(与 videoprt 的协作)

5.1 INT 10 接口

VBE 规范定义 BIOS 调用通过 INT 10 中断 完成,中断号 0x10,功能号在 AX 寄存器中:

  • AX=0x4F00 获取控制器信息
  • AX=0x4F01 获取模式信息
  • AX=0x4F02 设置 VBE 模式
  • AX=0x4F03 获取当前模式
  • AX=0x4F09 设置/获取调色板
  • AX=0x4F10 电源管理
  • AX=0x4F15 DDC(显示器数据通道)

5.2 V86 模式回退

从串口日志可见:

复制代码
(win32ss/drivers/videoprt/videoprt.c:816) Using x86 emulator
(win32ss/drivers/videoprt/int10.c:781) Could not initialize the x86 emulator; falling back to V86 mode

videoprt 优先尝试用 x86 模拟器 (在保护模式下模拟实模式指令)执行 BIOS 调用;若初始化失败(如 CPU 不支持或配置问题),则回退到 V86 模式 (虚拟 8086 模式)执行。vgapnp 不关心底层实现方式,只通过统一的 VIDEO_PORT_INT10_INTERFACE 调用。

5.3 实模式内存通信

由于 V86 模式下只能访问第一个 1MB 物理内存,VBE 的数据交换(模式列表、模式信息、EDID)都通过跳板内存完成:

  1. Int10AllocateBuffer 在低 1MB 分配缓冲区
  2. 调用前 Int10WriteMemory 写入参数
  3. Int10CallBios 执行 BIOS
  4. Int10ReadMemory 读回结果

实模式内存通信数据流图:
VBE BIOS (INT 10) 跳板内存 (低 1MB 实模式地址) videoprt.sys (INT10 接口) vgapnp.sys (VBEInitialize 等) VBE BIOS (INT 10) 跳板内存 (低 1MB 实模式地址) videoprt.sys (INT10 接口) vgapnp.sys (VBEInitialize 等) #mermaid-svg-IIBJwU80JYvevjpS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IIBJwU80JYvevjpS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IIBJwU80JYvevjpS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IIBJwU80JYvevjpS .error-icon{fill:#552222;}#mermaid-svg-IIBJwU80JYvevjpS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IIBJwU80JYvevjpS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IIBJwU80JYvevjpS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IIBJwU80JYvevjpS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IIBJwU80JYvevjpS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IIBJwU80JYvevjpS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IIBJwU80JYvevjpS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IIBJwU80JYvevjpS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IIBJwU80JYvevjpS .marker.cross{stroke:#333333;}#mermaid-svg-IIBJwU80JYvevjpS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IIBJwU80JYvevjpS p{margin:0;}#mermaid-svg-IIBJwU80JYvevjpS .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IIBJwU80JYvevjpS text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-IIBJwU80JYvevjpS .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-IIBJwU80JYvevjpS .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-IIBJwU80JYvevjpS .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-IIBJwU80JYvevjpS .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-IIBJwU80JYvevjpS #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-IIBJwU80JYvevjpS .sequenceNumber{fill:white;}#mermaid-svg-IIBJwU80JYvevjpS #sequencenumber{fill:#333;}#mermaid-svg-IIBJwU80JYvevjpS #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-IIBJwU80JYvevjpS .messageText{fill:#333;stroke:none;}#mermaid-svg-IIBJwU80JYvevjpS .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IIBJwU80JYvevjpS .labelText,#mermaid-svg-IIBJwU80JYvevjpS .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-IIBJwU80JYvevjpS .loopText,#mermaid-svg-IIBJwU80JYvevjpS .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-IIBJwU80JYvevjpS .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-IIBJwU80JYvevjpS .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-IIBJwU80JYvevjpS .noteText,#mermaid-svg-IIBJwU80JYvevjpS .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-IIBJwU80JYvevjpS .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IIBJwU80JYvevjpS .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IIBJwU80JYvevjpS .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IIBJwU80JYvevjpS .actorPopupMenu{position:absolute;}#mermaid-svg-IIBJwU80JYvevjpS .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-IIBJwU80JYvevjpS .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IIBJwU80JYvevjpS .actor-man circle,#mermaid-svg-IIBJwU80JYvevjpS line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-IIBJwU80JYvevjpS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Int10AllocateBuffer 返回 Seg:Off (段:偏移) Int10WriteMemory 写入参数 (如 "VBE2" 签名 / CLUT 数据) Int10CallBios (AX=0x4F00/0x4F01/0x4F02...) 切换到实模式/V86 执行 INT 10 BIOS 从 ES:DI 读取参数 BIOS 将结果写入 ES:DI 缓冲区 (VBE_INFO / VBE_MODEINFO / EDID) 返回 AX (0x004F=成功) Int10CallBios 返回 Int10ReadMemory 读回结果 数据 (模式信息/EDID)

5.4 INT 10 调用时序(以 0x4F00 为例)


6. 与 VirtualBox 的配合

6.1 VirtualBox 虚拟显卡与 VBE

VirtualBox 的默认虚拟显卡(VBoxVGA)实现了一个 VBE 兼容 的显示适配器:

  • 提供标准的 VBE 2.0/3.0 BIOS(通过 VGA BIOS ROM)
  • 支持线性帧缓冲(LFB)模式
  • 支持常见的 640x480 / 800x600 / 1024x768 / 1280x1024 / 1920x1080 等分辨率,8/16/24/32 位色深
  • 显存大小可在 VM 设置中配置(默认 16MB)

vgapnp.sys 正是通过 VBE 标准与 VirtualBox 虚拟显卡通信:

  1. VBEInitialize 调用 0x4F00 获取控制器信息 → 验证 "VESA" 签名和版本
  2. 枚举模式列表,筛选满足条件(≥640x480、线性帧缓冲、打包像素/直接色)的模式
  3. VBEMapVideoMemoryPhysBasePtr(VirtualBox 提供 LFB 物理地址)映射到内核虚拟地址
  4. framebuf.dll 直接向映射后的帧缓冲写入像素数据,由 VirtualBox 显示

vgapnp.sys ↔ VirtualBox 虚拟显卡通信数据流图:
#mermaid-svg-odD17A7CDHroIuty{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-odD17A7CDHroIuty .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-odD17A7CDHroIuty .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-odD17A7CDHroIuty .error-icon{fill:#552222;}#mermaid-svg-odD17A7CDHroIuty .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-odD17A7CDHroIuty .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-odD17A7CDHroIuty .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-odD17A7CDHroIuty .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-odD17A7CDHroIuty .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-odD17A7CDHroIuty .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-odD17A7CDHroIuty .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-odD17A7CDHroIuty .marker{fill:#333333;stroke:#333333;}#mermaid-svg-odD17A7CDHroIuty .marker.cross{stroke:#333333;}#mermaid-svg-odD17A7CDHroIuty svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-odD17A7CDHroIuty p{margin:0;}#mermaid-svg-odD17A7CDHroIuty .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-odD17A7CDHroIuty .cluster-label text{fill:#333;}#mermaid-svg-odD17A7CDHroIuty .cluster-label span{color:#333;}#mermaid-svg-odD17A7CDHroIuty .cluster-label span p{background-color:transparent;}#mermaid-svg-odD17A7CDHroIuty .label text,#mermaid-svg-odD17A7CDHroIuty span{fill:#333;color:#333;}#mermaid-svg-odD17A7CDHroIuty .node rect,#mermaid-svg-odD17A7CDHroIuty .node circle,#mermaid-svg-odD17A7CDHroIuty .node ellipse,#mermaid-svg-odD17A7CDHroIuty .node polygon,#mermaid-svg-odD17A7CDHroIuty .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-odD17A7CDHroIuty .rough-node .label text,#mermaid-svg-odD17A7CDHroIuty .node .label text,#mermaid-svg-odD17A7CDHroIuty .image-shape .label,#mermaid-svg-odD17A7CDHroIuty .icon-shape .label{text-anchor:middle;}#mermaid-svg-odD17A7CDHroIuty .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-odD17A7CDHroIuty .rough-node .label,#mermaid-svg-odD17A7CDHroIuty .node .label,#mermaid-svg-odD17A7CDHroIuty .image-shape .label,#mermaid-svg-odD17A7CDHroIuty .icon-shape .label{text-align:center;}#mermaid-svg-odD17A7CDHroIuty .node.clickable{cursor:pointer;}#mermaid-svg-odD17A7CDHroIuty .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-odD17A7CDHroIuty .arrowheadPath{fill:#333333;}#mermaid-svg-odD17A7CDHroIuty .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-odD17A7CDHroIuty .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-odD17A7CDHroIuty .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-odD17A7CDHroIuty .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-odD17A7CDHroIuty .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-odD17A7CDHroIuty .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-odD17A7CDHroIuty .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-odD17A7CDHroIuty .cluster text{fill:#333;}#mermaid-svg-odD17A7CDHroIuty .cluster span{color:#333;}#mermaid-svg-odD17A7CDHroIuty div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-odD17A7CDHroIuty .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-odD17A7CDHroIuty rect.text{fill:none;stroke-width:0;}#mermaid-svg-odD17A7CDHroIuty .icon-shape,#mermaid-svg-odD17A7CDHroIuty .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-odD17A7CDHroIuty .icon-shape p,#mermaid-svg-odD17A7CDHroIuty .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-odD17A7CDHroIuty .icon-shape .label rect,#mermaid-svg-odD17A7CDHroIuty .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-odD17A7CDHroIuty .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-odD17A7CDHroIuty .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-odD17A7CDHroIuty :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} VirtualBox 宿主机
ReactOS (虚拟机内)
虚拟显卡 (VBoxVGA)
虚拟 VBE BIOS ROM
vgapnp.sys (VBE miniport)
framebuf.dll
INT 10 调用
INT 10 调用
模式列表/模式信息
模式信息
PhysBasePtr 映射
IOCTL 查询/设置模式
直接写帧缓冲
DrvEnablePDEV

查询/选择模式
DrvEnableSurface

写像素数据
VBEInitialize

0x4F00 控制器信息
枚举模式 0x4F01
VBEMapVideoMemory

映射 LFB
INT 10 中断服务

VBE 2.0/3.0 实现
显存 (VRAM)

PhysBasePtr 指向
显示输出 → 宿主窗口

6.2 注册表配置

vbemp_reg.inf 将驱动注册为 服务名 "vga"(ImagePath 指向 vgapnp.sys),并创建显示设备配置:

复制代码
HKLM\SYSTEM\CurrentControlSet\Services\vga
  ErrorControl = 0
  Group = "Video Save"
  ImagePath = "system32\drivers\vgapnp.sys"
  Start = 1 (SYSTEM_START)
  Type = 1 (SERVICE_KERNEL_DRIVER)

HKLM\SYSTEM\CurrentControlSet\Services\vga\Device0
  InstalledDisplayDrivers = "framebuf"      ← 显示驱动 DDI 为 framebuf
  DefaultSettings.BitsPerPel = 32            ← 默认 32 位色
  DefaultSettings.XResolution = 800
  DefaultSettings.YResolution = 600
  Device Description = "Generic VESA Adapter"
  VgaCompatible = 1

关键点:

  • InstalledDisplayDrivers = "framebuf" --- 明确指定配套的显示驱动 DDI 是 framebuf.dll,win32k 的 InitDisplayDriver 正是读取此值
  • VgaCompatible = 1 --- 标记为 VGA 兼容设备(此值实际为真假的 hack,目的是允许用户用第三方驱动替换)
  • 服务名为 "vga" 是历史遗留(早期 VGA 驱动的服务名),实际的驱动文件是 vgapnp.sys

7. 数据流总览

7.1 启动流程

复制代码
系统引导 → videoprt.sys 加载(服务 vga 依赖)
   ↓ 端口驱动加载 vgapnp.sys (DriverEntry)
   ↓ VideoPortInitialize → 调用 VBEFindAdapter(只报告 VDM 内存范围)
   ↓ 初始化 → VBEInitialize
   │   ├── 获取 INT10 接口
   │   ├── 分配跳板内存
   │   ├── 0x4F00 获取控制器信息(验证 "VESA")
   │   ├── 枚举 + 筛选模式列表(≥640x480, LFB, 打包/直接色)
   │   ├── 冒泡排序模式
   │   └── VideoPortEnumerateChildren(EDID 读取)
   ↓
win32k 读取注册表 InstalledDisplayDrivers="framebuf"
   ↓ 加载 framebuf.dll → DrvEnablePDEV
   ↓ framebuf 通过 EngDeviceIoControl 发 IOCTL:
   │   ├── IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES / QUERY_AVAIL_MODES
   │   ├── IOCTL_VIDEO_SET_CURRENT_MODE
   │   └── IOCTL_VIDEO_MAP_VIDEO_MEMORY → 获得帧缓冲地址
   ↓ framebuf 创建显示表面 (DrvEnableSurface)
   ↓ 桌面渲染 → framebuf 写帧缓冲 → VirtualBox 显示

启动流程时序图:
framebuf.dll win32k.sys VBE BIOS (INT 10) vgapnp.sys (VBE miniport) videoprt.sys 端口驱动 系统引导/即插即用 framebuf.dll win32k.sys VBE BIOS (INT 10) vgapnp.sys (VBE miniport) videoprt.sys 端口驱动 系统引导/即插即用 #mermaid-svg-ACoekyKKh49D23b8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ACoekyKKh49D23b8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ACoekyKKh49D23b8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ACoekyKKh49D23b8 .error-icon{fill:#552222;}#mermaid-svg-ACoekyKKh49D23b8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ACoekyKKh49D23b8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ACoekyKKh49D23b8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ACoekyKKh49D23b8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ACoekyKKh49D23b8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ACoekyKKh49D23b8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ACoekyKKh49D23b8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ACoekyKKh49D23b8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ACoekyKKh49D23b8 .marker.cross{stroke:#333333;}#mermaid-svg-ACoekyKKh49D23b8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ACoekyKKh49D23b8 p{margin:0;}#mermaid-svg-ACoekyKKh49D23b8 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ACoekyKKh49D23b8 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-ACoekyKKh49D23b8 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ACoekyKKh49D23b8 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-ACoekyKKh49D23b8 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-ACoekyKKh49D23b8 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-ACoekyKKh49D23b8 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-ACoekyKKh49D23b8 .sequenceNumber{fill:white;}#mermaid-svg-ACoekyKKh49D23b8 #sequencenumber{fill:#333;}#mermaid-svg-ACoekyKKh49D23b8 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-ACoekyKKh49D23b8 .messageText{fill:#333;stroke:none;}#mermaid-svg-ACoekyKKh49D23b8 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ACoekyKKh49D23b8 .labelText,#mermaid-svg-ACoekyKKh49D23b8 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-ACoekyKKh49D23b8 .loopText,#mermaid-svg-ACoekyKKh49D23b8 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-ACoekyKKh49D23b8 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ACoekyKKh49D23b8 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-ACoekyKKh49D23b8 .noteText,#mermaid-svg-ACoekyKKh49D23b8 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-ACoekyKKh49D23b8 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ACoekyKKh49D23b8 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ACoekyKKh49D23b8 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ACoekyKKh49D23b8 .actorPopupMenu{position:absolute;}#mermaid-svg-ACoekyKKh49D23b8 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-ACoekyKKh49D23b8 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ACoekyKKh49D23b8 .actor-man circle,#mermaid-svg-ACoekyKKh49D23b8 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-ACoekyKKh49D23b8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} loop 每个模式 桌面渲染 → 写帧缓冲 → VirtualBox 显示 加载 videoprt.sys 加载 vgapnp.sys → DriverEntry VideoPortInitialize (注册回调表) VBEFindAdapter → 报告 VDM 内存 (0xA0000) VBEInitialize VideoPortQueryServices (INT10 接口) Int10CallBios 0x4F00 (控制器信息) "VESA" 签名 + 版本 + 模式指针 Int10CallBios 0x4F01 (模式信息) 分辨率/位深/LFB 地址 筛选+排序模式列表 VideoPortEnumerateChildren → EDID 读注册表 InstalledDisplayDrivers="framebuf" 加载 framebuf.dll → DrvEnablePDEV IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES IOCTL_VIDEO_QUERY_AVAIL_MODES IOCTL_VIDEO_SET_CURRENT_MODE Int10CallBios 0x4F02 (设置模式) IOCTL_VIDEO_MAP_VIDEO_MEMORY 帧缓冲虚拟地址 + 长度 DrvEnableSurface 创建显示表面

7.2 运行时渲染路径

复制代码
应用程序 → GDI32 → win32k (ntgdi) → GDI 引擎 (eng/)
   → framebuf.dll (DrvBitBlt/DrvTextOut 等 DDI 回调)
   → 直接写入映射的帧缓冲 (VideoRamBase)
   → VirtualBox 将帧缓冲内容显示到宿主机窗口

运行时渲染数据流图:
#mermaid-svg-eEIm0FOupqm64jBm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-eEIm0FOupqm64jBm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eEIm0FOupqm64jBm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eEIm0FOupqm64jBm .error-icon{fill:#552222;}#mermaid-svg-eEIm0FOupqm64jBm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eEIm0FOupqm64jBm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eEIm0FOupqm64jBm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eEIm0FOupqm64jBm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eEIm0FOupqm64jBm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eEIm0FOupqm64jBm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eEIm0FOupqm64jBm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eEIm0FOupqm64jBm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eEIm0FOupqm64jBm .marker.cross{stroke:#333333;}#mermaid-svg-eEIm0FOupqm64jBm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eEIm0FOupqm64jBm p{margin:0;}#mermaid-svg-eEIm0FOupqm64jBm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-eEIm0FOupqm64jBm .cluster-label text{fill:#333;}#mermaid-svg-eEIm0FOupqm64jBm .cluster-label span{color:#333;}#mermaid-svg-eEIm0FOupqm64jBm .cluster-label span p{background-color:transparent;}#mermaid-svg-eEIm0FOupqm64jBm .label text,#mermaid-svg-eEIm0FOupqm64jBm span{fill:#333;color:#333;}#mermaid-svg-eEIm0FOupqm64jBm .node rect,#mermaid-svg-eEIm0FOupqm64jBm .node circle,#mermaid-svg-eEIm0FOupqm64jBm .node ellipse,#mermaid-svg-eEIm0FOupqm64jBm .node polygon,#mermaid-svg-eEIm0FOupqm64jBm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eEIm0FOupqm64jBm .rough-node .label text,#mermaid-svg-eEIm0FOupqm64jBm .node .label text,#mermaid-svg-eEIm0FOupqm64jBm .image-shape .label,#mermaid-svg-eEIm0FOupqm64jBm .icon-shape .label{text-anchor:middle;}#mermaid-svg-eEIm0FOupqm64jBm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-eEIm0FOupqm64jBm .rough-node .label,#mermaid-svg-eEIm0FOupqm64jBm .node .label,#mermaid-svg-eEIm0FOupqm64jBm .image-shape .label,#mermaid-svg-eEIm0FOupqm64jBm .icon-shape .label{text-align:center;}#mermaid-svg-eEIm0FOupqm64jBm .node.clickable{cursor:pointer;}#mermaid-svg-eEIm0FOupqm64jBm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-eEIm0FOupqm64jBm .arrowheadPath{fill:#333333;}#mermaid-svg-eEIm0FOupqm64jBm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-eEIm0FOupqm64jBm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-eEIm0FOupqm64jBm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eEIm0FOupqm64jBm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-eEIm0FOupqm64jBm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eEIm0FOupqm64jBm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-eEIm0FOupqm64jBm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eEIm0FOupqm64jBm .cluster text{fill:#333;}#mermaid-svg-eEIm0FOupqm64jBm .cluster span{color:#333;}#mermaid-svg-eEIm0FOupqm64jBm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-eEIm0FOupqm64jBm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-eEIm0FOupqm64jBm rect.text{fill:none;stroke-width:0;}#mermaid-svg-eEIm0FOupqm64jBm .icon-shape,#mermaid-svg-eEIm0FOupqm64jBm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eEIm0FOupqm64jBm .icon-shape p,#mermaid-svg-eEIm0FOupqm64jBm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-eEIm0FOupqm64jBm .icon-shape .label rect,#mermaid-svg-eEIm0FOupqm64jBm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eEIm0FOupqm64jBm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-eEIm0FOupqm64jBm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-eEIm0FOupqm64jBm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 硬件/宿主
内核模式 (win32k.sys)
用户模式
设备能力检查

硬件加速?
应用程序

(DrawText / BitBlt / Rectangle)
gdi32.dll

(NtGdi* 系统调用)
ntgdi

(对象管理/坐标变换/裁剪)
GDI 引擎 eng/

(EngBitBlt/EngTextOut)
framebuf.dll

(DrvBitBlt/DrvTextOut DDI)
映射的帧缓冲内存

(VBEMapVideoMemory 结果)
VirtualBox 虚拟显卡 VRAM
宿主窗口显示


8. 驱动内部调用关系图

复制代码
DriverEntry
  └── VideoPortInitialize
        ├── VBEFindAdapter         (HwFindAdapter)
        ├── VBEInitialize          (HwInitialize)
        │     ├── VideoPortQueryServices (INT10)
        │     ├── Int10AllocateBuffer
        │     ├── Int10CallBios (0x4F00 控制器信息)
        │     ├── Int10ReadMemory (模式列表)
        │     ├── Int10CallBios (0x4F01 每个模式信息)
        │     ├── VBESortModes
        │     └── VideoPortEnumerateChildren → VBEGetVideoChildDescriptor (EDID)
        ├── VBEStartIO             (HwStartIO) ← 所有 IOCTL 入口
        │     ├── VBESetCurrentMode       (0x4F02)
        │     ├── VBEResetDevice          (0x4F02, 模式3)
        │     ├── VBEMapVideoMemory       (VideoPortMapMemory)
        │     ├── VBEUnmapVideoMemory     (VideoPortUnmapMemory)
        │     ├── VBEQueryNumAvailModes
        │     ├── VBEQueryAvailModes → VBEQueryMode
        │     ├── VBEQueryCurrentMode → VBEQueryMode
        │     └── VBESetColorRegisters    (端口写 或 0x4F09)
        ├── VBEResetHw             (HwResetHw)
        ├── VBEGetPowerState       (HwGetPowerState, 0x4F10)
        └── VBESetPowerState       (HwSetPowerState, 0x4F10)

驱动内部调用关系图(Mermaid):
#mermaid-svg-YOZTeV9vlz54zSIX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YOZTeV9vlz54zSIX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YOZTeV9vlz54zSIX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YOZTeV9vlz54zSIX .error-icon{fill:#552222;}#mermaid-svg-YOZTeV9vlz54zSIX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YOZTeV9vlz54zSIX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YOZTeV9vlz54zSIX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YOZTeV9vlz54zSIX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YOZTeV9vlz54zSIX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YOZTeV9vlz54zSIX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YOZTeV9vlz54zSIX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YOZTeV9vlz54zSIX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YOZTeV9vlz54zSIX .marker.cross{stroke:#333333;}#mermaid-svg-YOZTeV9vlz54zSIX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YOZTeV9vlz54zSIX p{margin:0;}#mermaid-svg-YOZTeV9vlz54zSIX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YOZTeV9vlz54zSIX .cluster-label text{fill:#333;}#mermaid-svg-YOZTeV9vlz54zSIX .cluster-label span{color:#333;}#mermaid-svg-YOZTeV9vlz54zSIX .cluster-label span p{background-color:transparent;}#mermaid-svg-YOZTeV9vlz54zSIX .label text,#mermaid-svg-YOZTeV9vlz54zSIX span{fill:#333;color:#333;}#mermaid-svg-YOZTeV9vlz54zSIX .node rect,#mermaid-svg-YOZTeV9vlz54zSIX .node circle,#mermaid-svg-YOZTeV9vlz54zSIX .node ellipse,#mermaid-svg-YOZTeV9vlz54zSIX .node polygon,#mermaid-svg-YOZTeV9vlz54zSIX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YOZTeV9vlz54zSIX .rough-node .label text,#mermaid-svg-YOZTeV9vlz54zSIX .node .label text,#mermaid-svg-YOZTeV9vlz54zSIX .image-shape .label,#mermaid-svg-YOZTeV9vlz54zSIX .icon-shape .label{text-anchor:middle;}#mermaid-svg-YOZTeV9vlz54zSIX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YOZTeV9vlz54zSIX .rough-node .label,#mermaid-svg-YOZTeV9vlz54zSIX .node .label,#mermaid-svg-YOZTeV9vlz54zSIX .image-shape .label,#mermaid-svg-YOZTeV9vlz54zSIX .icon-shape .label{text-align:center;}#mermaid-svg-YOZTeV9vlz54zSIX .node.clickable{cursor:pointer;}#mermaid-svg-YOZTeV9vlz54zSIX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YOZTeV9vlz54zSIX .arrowheadPath{fill:#333333;}#mermaid-svg-YOZTeV9vlz54zSIX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YOZTeV9vlz54zSIX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YOZTeV9vlz54zSIX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YOZTeV9vlz54zSIX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YOZTeV9vlz54zSIX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YOZTeV9vlz54zSIX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YOZTeV9vlz54zSIX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YOZTeV9vlz54zSIX .cluster text{fill:#333;}#mermaid-svg-YOZTeV9vlz54zSIX .cluster span{color:#333;}#mermaid-svg-YOZTeV9vlz54zSIX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-YOZTeV9vlz54zSIX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YOZTeV9vlz54zSIX rect.text{fill:none;stroke-width:0;}#mermaid-svg-YOZTeV9vlz54zSIX .icon-shape,#mermaid-svg-YOZTeV9vlz54zSIX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YOZTeV9vlz54zSIX .icon-shape p,#mermaid-svg-YOZTeV9vlz54zSIX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YOZTeV9vlz54zSIX .icon-shape .label rect,#mermaid-svg-YOZTeV9vlz54zSIX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YOZTeV9vlz54zSIX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YOZTeV9vlz54zSIX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YOZTeV9vlz54zSIX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} SET_CURRENT_MODE
RESET_DEVICE
MAP_VIDEO_MEMORY
UNMAP_VIDEO_MEMORY
QUERY_NUM_AVAIL_MODES
QUERY_AVAIL_MODES
QUERY_CURRENT_MODE
SET_COLOR_REGISTERS
非VGA
VGA兼容
ChildIndex==1
失败
DriverEntry

注册 VIDEO_HW_INITIALIZATION_DATA
VideoPortInitialize
VBEFindAdapter

(HwFindAdapter)
VBEInitialize

(HwInitialize)
VBEStartIO

(HwStartIO)
VBEResetHw

(HwResetHw)
VBEGetPowerState

(HwGetPowerState)
VBESetPowerState

(HwSetPowerState)
VBEGetVideoChildDescriptor

(HwGetVideoChildDescriptor)
VideoPortQueryServices

INT10 接口
Int10AllocateBuffer
Int10CallBios 0x4F00

控制器信息
Int10ReadMemory

模式列表
Int10CallBios 0x4F01

每个模式信息
VBESortModes
VideoPortEnumerateChildren
VBESetCurrentMode

0x4F02
VBEResetDevice

0x4F02 模式3
VBEMapVideoMemory

VideoPortMapMemory
VBEUnmapVideoMemory
VBEQueryNumAvailModes
VBEQueryAvailModes
VBEQueryCurrentMode
VBESetColorRegisters
VBEQueryMode
Int10CallBios 0x4F09
写端口 0x3C8/0x3C9
VBEReadEdid 直接 DDC
VBEReadEdidUsingSCI

I2C 位敲打


9. 设计特点总结

  1. 纯标准接口实现:完全通过 VBE 规范和 VideoPort 服务函数工作,不依赖具体芯片厂商------这是它能驱动任何 VBE 兼容显卡(包括 VirtualBox、QEMU、Bochs 虚拟显卡)的关键。
  2. 延迟检测VBEFindAdapter 不做真实检测(早期无法用 INT 10),把检测推迟到 VBEInitialize
  3. 线性帧缓冲优先 :默认要求 VBE 2.0+ 且支持 LFB(bit14 模式号),VBE 1.2 窗口模式支持被编译选项 VBE12_SUPPORT 关闭。
  4. 模式筛选严格:只保留 ≥640x480、打包像素/直接色模型、有物理帧缓冲地址的模式。
  5. 双路径调色板:VGA 兼容控制器直接写 DAC 端口(对 Bochs 等虚拟显卡必要),否则走 BIOS 调色板函数。
  6. EDID 双方法:优先直接 DDC 读取,失败则用 I2C 位敲打方式。
  7. 无硬件私有状态:除了 INT10 接口和模式表,驱动不保存任何芯片级状态,模式切换全部委托 BIOS。

10. 源码索引

文件 关键内容
vbemp.c(file:///d:/reactos/win32ss/drivers/miniport/vbe/vbemp.c) DriverEntry、VBEFindAdapter、VBEInitialize、VBEStartIO、模式管理、内存映射、调色板、电源
vbemp.h(file:///d:/reactos/win32ss/drivers/miniport/vbe/vbemp.h) VBE_INFO、VBE_MODEINFO、VBE_DEVICE_EXTENSION、VBE 命令定义
edid.c(file:///d:/reactos/win32ss/drivers/miniport/vbe/edid.c) VBEGetVideoChildDescriptor、VBEReadEdid、VBEReadEdidUsingSCI
vbemp_reg.inf(file:///d:/reactos/win32ss/drivers/miniport/vbe/vbemp_reg.inf) 服务 "vga" 注册、Device0 配置、InstalledDisplayDrivers=framebuf
CMakeLists.txt(file:///d:/reactos/win32ss/drivers/miniport/vbe/CMakeLists.txt) 构建配置(模块名 vgapnp、导入 videoprt)

关联模块:

  • 视频端口驱动:videoprt/(file:///d:/reactos/win32ss/drivers/videoprt/videoprt.c)(提供 VideoPort* 服务和 INT10 接口)
  • 显示驱动 DDI:framebuf/(file:///d:/reactos/win32ss/drivers/displays/framebuf/enable.c)(消费 vgapnp 提供的模式与帧缓冲)
  • win32k 驱动加载:display.c(file:///d:/reactos/win32ss/user/ntuser/display.c)(读取 InstalledDisplayDrivers)

本文档基于 ReactOS 源代码 win32ss/drivers/miniport/vbe/ 分析(2026 年 8 月)

相关推荐
ZJU_统一阿萨姆9 小时前
【推理优化】连续批处理:当推理吞吐量遇到天花板
人工智能·语言模型·系统架构
caimouse9 小时前
ReactOS 图形系统分析(4):GDI 引擎 (eng)
系统架构
caimouse10 小时前
ReactOS 图形系统分析(9):物理设备对象 — PDEVOBJ
系统架构
caimouse11 小时前
ReactOS 图形系统分析(11):映射设备对象 — MDEVOBJ(多显示器)
系统架构·计算机外设
国科安芯1 天前
详解ADC 规则组外部触发:CH2、CH3 与 TRGO2
运维·网络·单片机·嵌入式硬件·mcu·系统架构
Dr.kangder1 天前
嵌入式面试总结(十)——流水线
单片机·嵌入式硬件·面试·职场和发展·系统架构·嵌入式
ZJU_统一阿萨姆1 天前
【推理优化】推理的本质:prefill 与 decode 两阶段
语言模型·系统架构
郑州光合科技余经理1 天前
餐饮预定系统架构拆解:订单链路、权限组织与私有化源码交付
java·开发语言·前端·数据库·人工智能·系统架构·php
youngerwang2 天前
【软件系统架构师案例分析每日深耕 Day 13】多质量属性权衡:电商大促架构设计
系统架构·案例分析·电商大促·多质量属性