BIOS开发笔记 14 – WMI 在系统中的集成及应用(上)

作为系统开发者和原始设备制造商(OEM),我们经常需要将硬件和固件的数据以标准化的方式暴露给用户空间应用程序。在 Windows 系统上,通过将 ACPI(高级配置与电源接口) 与 WMI(Windows 管理规范) 结合起来,可以实现这一目标。本文将结合微软 WMI 文档深入探讨 OEM 如何通过在系统中嵌入 ACPI 对象,利用 ACPI-to-WMI 映射驱动程序,使硬件监控数据对管理应用程序可用。文章分为上下两篇,第一篇主要讲理论部分,第二篇则用实例介绍如何在固件中集成 WMI 以及在系统下调用。

ACPI 与 WMI 简介

ACPI 是一个广泛采用的行业标准,定义了硬件和固件如何与操作系统交互,主要用于电源管理和设备配置。但它的功能远不止于此——ACPI 还提供了丰富的监控数据,例如温度区域、电池状态,甚至 OEM 自定义的指标。然而,这些数据并不能直接被用户态的应用程序访问。

这时,WMI 登场了,它提供了一种统一的方式来管理和监控系统资源。通过将 ACPI 与 WMI 桥接,OEM 可以无需为每个功能编写专用驱动程序,就能将硬件层的数据暴露给用户态工具。这背后的关键在于两个驱动程序——Acpi.sys 和 Wmiacpi.sys,再加上一些 ACPI 源语言(ASL) 代码和 托管对象格式(MOF) 文件。

技术架构:它是如何工作的

核心组件

ACPI-to-WMI 映射功能依赖于 Windows 支持的两个驱动程序:

  • Acpi.sys:标准的 ACPI 驱动程序,经过少量修改以支持监控任务。它负责执行 ASL 代码并与硬件进行低级交互。
  • Wmiacpi.sys:一个映射驱动程序,注册了 Plug and Play ID PNP0C14。它充当桥梁,将 ACPI 数据和事件翻译为 WMI 可理解的格式。

OEM 通过以下方式增强这一系统:

  • 编写 ASL 代码 来定义 ACPI 对象和控制方法。
  • 提供 MOF 文件(可以嵌入 BIOS 或存储在磁盘上),描述通过 WMI 暴露的数据结构和方法。

ASL 与 WMI 的交互

Wmiacpi.sys 不会直接执行 ASL 代码,而是调用 Acpi.sys 来运行控制方法以访问监控数据。这种分工确保了 ACPI 的核心功能保持完整,而映射驱动程序专注于将数据暴露给 WMI 消费者。

例如,Microsoft 并未为 Wmiacpi.sys 提供默认的 MOF 文件。唯一默认暴露的监控数据是温度区域信息,由 Acpi.sys 直接处理。对于其他内容——如自定义数据块、方法或事件——OEM 需要自己动手实现。

WMI 监控基础

WMI 将数据组织为 数据块(类似于 C 中的结构体),每个数据块包含相关的 数据项(属性)。以下是关键概念的简要概述:

  • 数据块:由 128 位 GUID 命名,可以有多个实例,包含一个或多个数据项。
  • 数据项:在数据块内有唯一索引,是具体的信息单元(例如温度读数)。
  • 方法:功能上类似于 IOCTL,由 GUID 和方法索引标识,使用单一缓冲区处理输入/输出参数。
  • 事件:重要事件的通知,也由 GUID 命名,可选携带附加数据块。

WMI 支持:

  • 查询所有数据块实例或单个实例。
  • 设置数据块实例中的所有或单个数据项。
  • 注册特定条件发生时的事件通知。

这个架构的妙处在于它的可扩展性。OEM 可以定义自定义的数据块、方法和事件,并通过两位字符 ID(例如查询的 WQxx)将它们映射到 ACPI 控制方法。

加载和配置映射驱动程序

Plug and Play 集成

Wmiacpi.sys 驱动程序绑定到 Plug and Play ID PNP0C14。若要加载它,ACPI 设备树中必须定义一个或多个带有此 ID 的设备。每个设备都有自己的操作系统设备对象和映射集,便可以在设备树中逻辑地组织数据块。如果存在多个 PNP0C14 设备,每个设备必须具有唯一的 _UID 以避免冲突。

映射驱动程序工作流程

加载后,Wmiacpi.sys 会负责:

  1. 注册:通过 IRP_MN_REGINFO IRP 向 WMI 注册数据块、方法和事件。
  2. GUID 发现:查询 _WDG 控制方法以获取支持的 GUID 列表及其与 ACPI 控制方法 ID 的映射。
  3. 翻译:将 WMI 查询、设置和方法调用转换为 ACPI 控制方法调用。
  4. 事件处理:接收 ACPI 通知(例如 notify(mapper-device, 0x81)),并将其重新发布为 WMI 事件,可选通过 _WED 获取附加数据。
  5. 字符串转换:在 ACPI 的 ASCIZ 和 WMI 的 UNICODE 之间转换字符串数据块。

ACPI 控制方法:核心细节

映射驱动程序依赖一组标准化的 ACPI 控制方法,每个方法都有特定的命名约定和用途。以下是详细分解:

_WDG:数据块 GUID 映射

  • 用途:返回一个缓冲区,将 WMI GUID 映射到 ACPI 控制方法 ID 或通知值。
  • 结构
typedef struct {
    GUID guid;              // WMI GUID
    union {
        CHAR ObjectId[2];   // 两位字符 ACPI ID(例如 "QD")
        struct {
            UCHAR NotificationValue; // 用于事件
            UCHAR Reserved[1];
        } NotifyId;
    };
    USHORT InstanceCount;   // 实例数量
    USHORT Flags;           // 配置标志
};

 

  • 标志
    • WMIACPI_REGFLAG_EXPENSIVE = 1该数据块的收集是有开销的(expensive)。
    • WMIACPI_REGFLAG_METHOD = 2:表示方法而非数据块。
    • WMIACPI_REGFLAG_STRING = 4:数据块为字符串(需 ASCIZ-UNICODE 转换)。
    • WMIACPI_REGFLAG_EVENT = 8:映射到事件。

主要控制方法

方法
名称
参数
用途
是否必需?
查询
WQxx ULONG

(实例)
返回数据块内容
设置
WSxx ULONG

,缓冲区
设置数据块内容
否(只读可选)
方法
WMxx ULONG

,ID,缓冲区
执行方法
是(用于方法)
事件
WExx UCHAR

(0=禁用)
启用/禁用事件
否(若开销大)
收集
WCxx UCHAR

(0=禁用)
启用/禁用数据收集
否(若开销大)
事件数据
_WED
通知代码
提供附加事件数据
可选
  • WQxx:返回指定实例的数据块。字符串会转换为 UNICODE。
  • WSxx:设置数据块内容。字符串从 UNICODE 转换为 ASCIZ。
  • WMxx:执行方法,传递输入/输出缓冲区。支持字符串转换。
  • WExx 和 WCxx:可选方法,用于优化高开销操作的资源使用。
  • _WED:获取附加事件数据,包含在 WMI 事件负载中。

数据块和 MOF 文件设计

数据块设计建议

  • 组合只读数据:将相关的只读项合并到一个数据块。
  • 隔离字符串:使用单独的数据块存放字符串,并设置 WMIACPI_REGFLAG_STRING 标志。
  • 分离可设置项:将可单独设置的项放入独立数据块。

MOF 文件:定义模式

MOF 文件描述您的数据块、方法和事件,可以是:

  • 附加到 Wmiacpi.sys 或其他文件的资源(通过注册表 MofImagePath 指定)。
  • 由绑定到二进制 MOF GUID 的 WQxx 方法返回。

支持的数据类型

类型
格式
String
Null-terminated ANSI string
sint32
Signed 32-bit integer
uint32
Unsigned 32-bit integer
sint16
Signed 16-bit integer
uint16
Unsigned 16-bit integer
sint64
Signed 64-bit integer
uint64
Unsigned 64-bit integer
sint8
Signed 8-bit character
uint8
Unsigned 8-bit integer
Datetime
25-character string used to specify absolute dates or time intervals
Boolean
Byte where 0 is FALSE, != 0 is TRUE

类限定符:

  • [guid("guid字符串")]
    声明代表 WMI 类的唯一 GUID,此限定符为必需项。
  • [Dynamic]
    WBEM 协议必须限定符。
  • [WMI]
    WBEM 协议必须限定符。
  • [Provider("WmiProv")]
    WBEM 协议必须限定符。
  • [Description("描述文本")]
    为类或属性提供本地化描述文本,需配合区域限定符使用。
  • [WmiExpense(资源消耗值)]
    定义收集数据块所需的系统资源量,值为平均CPU周期数(默认0)。

数据项限定符:

  • [read]
    允许读取该数据项。
  • [write]
    允许写入该数据项。
  • [WmiDataID(数据项ID)]
    指定数据项的唯一标识符(从1开始),此限定符为必需项。
  • [WmiScale(缩放因子)]
    定义数据显示时的缩放比例(10的缩放因子次方,默认0)。
  • [WmiComplexity("复杂度等级")]
    设置指标详细程度:"Novice"(基础)、"Advanced"(高级)、"Expert"(专家)、"Wizard"(设计级),默认为"Novice"。
  • [WmiVolatility(有效间隔)]
    以毫秒为单位指定数据更新频率(如1000表示每秒更新),无默认值。
  • [WmiSizeIs("属性名")]
    指定当前类中用于记录变长数组元素数量的属性(非字节数)。

示例 MOF

[Dynamic, Provider("WmiProv"), WMI, guid("{12345678-...}")]
class MyDataBlock {
    [read, WmiDataID(1)] uint32 Temperature;
    [read, write, WmiDataID(2)] string Status;
};

实现步骤

  1. 定义 ACPI 设备:在 ASL 中添加 PNP0C14 设备,分配唯一的 _UID
  2. 编写 ASL 代码:实现 _WDG 及支持方法(WQxxWMxx 等)。
  3. 创建 MOF:定义数据块并编译为 BMF 资源。
  4. 使用 WMI 工具测试:通过 wbemtest 等工具查询数据块和事件。

总结

通过将 ACPI 与 WMI 结合,OEM 可以在 Windows 上实现一个灵活而强大的系统监控方案。无论是暴露温度数据、自定义指标还是事件通知,Wmiacpi.sys 驱动程序提供了一个通用的可扩展框架。

引用

1.Windows Instrumentation: WMI and ACPI

版权声明:
作者:bin
链接:https://ay123.net/mystudy/bios/1859/
来源:爱影博客
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
海报
BIOS开发笔记 14 – WMI 在系统中的集成及应用(上)
作为系统开发者和原始设备制造商(OEM),我们经常需要将硬件和固件的数据以标准化的方式暴露给用户空间应用程序。在 Windows 系统上,通过将 ACPI(高级配置……
<<上一篇
下一篇>>
文章目录
关闭
目 录