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
相关产品推荐
相关产品推荐

