You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

gnu-efi调用CMOS读写时InitializeGlobalIoDevice使用方法

gnu-efi环境下CMOS读写调用InitializeGlobalIoDevice参数说明

CMOS通过x86平台标准IO端口0x70(地址索引寄存器)、0x71(数据寄存器)访问,两个端口挂在LPC/ISA桥下,需要拿到对应设备的EFI_DEVICE_IO_INTERFACE实例才能完成端口读写,InitializeGlobalIoDevice的入参取值如下:

参数取值规则

  • DevicePath参数:传入LPC桥的设备路径即可。x86通用PC平台的LPC桥固定挂在PciRoot(0)总线的0x1f号设备、0号功能下,直接构造对应PCI设备路径传入即可,gnu-efi下可以直接用设备路径节点静态构造。
  • Protocol参数:该参数是函数自带的递归加载检查用占位参数,仅用于判断当前路径上是否已经安装了目标驱动协议、避免重复加载。应用层调用拿IO接口时,传入一个系统内全局不存在的自定义占位GUID即可,保证第一次LocateDevicePath调用返回未找到,函数就会正常进入后续流程,查找路径上的DeviceIoProtocol实例返回。
  • ErrorStr参数:传入任意ASCII字符串即可,仅用于初始化报错时打印调试日志,不影响实际功能。

调用示例代码

#include <efi.h>
#include <efilib.h>

EFI_DEVICE_IO_INTERFACE *gCmosIo = NULL;

// 自定义占位GUID,保证系统中无对应协议即可,值可随意修改
EFI_GUID const mCmosPlaceholderGuid = {0x88888888, 0x1234, 0x5678, {0xaa,0xbb,0xcc,0xdd,0xee,0xff,0x00,0x11}};

EFI_STATUS InitCmosIo(VOID)
{
    // 静态构造x86标准LPC桥设备路径 PciRoot(0)/Pci(0x1f,0)
    EFI_DEVICE_PATH const lpcPath[] = {
        {
            HARDWARE_DEVICE_PATH,
            HW_PCI_ROOT_DP,
            { (UINT8)sizeof(PCI_ROOT_DEVICE_PATH), (UINT8)(sizeof(PCI_ROOT_DEVICE_PATH) >> 8) }
        },
        0, // PciRoot 0
        {
            HARDWARE_DEVICE_PATH,
            HW_PCI_DP,
            { (UINT8)sizeof(PCI_DEVICE_PATH), (UINT8)(sizeof(PCI_DEVICE_PATH) >> 8) }
        },
        0,    // PCI Function 0
        0x1f, // PCI Device 31 标准LPC桥设备号
        END_DEVICE_PATH
    };

    return InitializeGlobalIoDevice(
        (EFI_DEVICE_PATH*)lpcPath,
        (EFI_GUID*)&mCmosPlaceholderGuid,
        "CMOS IO Device",
        &gCmosIo
    );
}

// CMOS读寄存器
UINT8 CmosReadReg(UINT8 reg)
{
    UINT8 val;
    // 写0x70地址寄存器时最高位置1,屏蔽NMI避免触发不可屏蔽中断异常
    UINT8 addrVal = reg | 0x80;
    gCmosIo->Io.Write(gCmosIo, IO_UINT8, 0x70, 1, &addrVal);
    gCmosIo->Io.Read(gCmosIo, IO_UINT8, 0x71, 1, &val);
    return val;
}

// CMOS写寄存器
VOID CmosWriteReg(UINT8 reg, UINT8 val)
{
    UINT8 addrVal = reg | 0x80;
    gCmosIo->Io.Write(gCmosIo, IO_UINT8, 0x70, 1, &addrVal);
    gCmosIo->Io.Write(gCmosIo, IO_UINT8, 0x71, 1, &val);
}

注意事项

  • 上述LPC桥的PCI位置0:1f.0是通用PC平台标准配置,少数定制嵌入式UEFI平台可能需要调整PCI设备号,可通过枚举PCI总线查找ISA/LPC桥类设备修正路径。
  • 0x70端口最高位为NMI控制位,写0会开启不可屏蔽中断,常规CMOS操作建议保持该位为1,避免误触发NMI导致系统崩溃。
  • 该方法仅能在UEFI启动服务阶段(调用ExitBootServices之前)使用,进入操作系统后启动服务被销毁,无法再通过UEFI接口访问IO端口。
  • 如果不需要适配InitializeGlobalIoDevice函数,直接调用BS->LocateProtocol(&gEfiDeviceIoProtocolGuid, NULL, (VOID**)&gCmosIo)拿到的DeviceIo实例,一般也可以直接访问CMOS端口,代码更简洁。

内容的提问来源于stack exchange,提问作者Tody Kwok

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 09:57:32