引言

在上一篇<ACPI.sys,从Windows到Bios的桥梁(2):Windows应用程序响应主板上GPIO(SCI)设备中断 硬件篇>只完成了2件事:

1.Bios接收GPIO SCI事件;

2.Bios转发SCI事件到ACPI命名空间\_GPE.Method(_L56),然后打印日志。

本文将处理从硬件层移至软件层,完成下列2步:

1.Method(_L56)通知ACPI.sys;

2.驱动接收及处理ACPI Notification(MSDN将这类来自BIOS的SCI事件称为ACPI Notification)。以下是实现该目标的步骤:

Step 1.创建承上启下的虚拟设备

设备驱动程序必然依存于某类设备(DeviceNode),这类设备或真实存在,或虚拟创建。虽然本段的标题写的是创建虚拟设备,但该虚拟设备不同于Winddk Sample中Toast创建的设备----Toast通过IoCreateDevice创建,而是由Bios在ACPI命名空间中创建的设备,希望读者加以区分。
那么这样的虚拟设备该怎么创建?ACPI Spec 5.4节有现成可用的例子!很多OEM厂商为了实现硬件增值功能就会创建这种虚拟设备,比如联想(联想打钱!)ThinkPad的"Lenovo PM Device"就属于此。我们可以借助RW utility查看它的实现:

嗯,大家可以参考ThinkPad,依葫芦画瓢可以写出这样的ASL code,编译进Bios开机过后,就能在设备管理器里找到带黄标的"Unknown Device":

Scope(\_SB) {Device(HAND) { #HAND->Han's deviceName(_HID, EISAID("HAN0000"))Method(_STA) {Return(0x0B)}}
}

查看其Hardware ID,和ASL Code中_HID对象指定的值一致:

Step 2.设计测试驱动,解决黄标

到这一步,已经进入到读者熟悉的软件领域,但是工作量仍然不小。

首先来解决黄标问题。根据设备管理器信息可知黄标是由于Bios虚拟的设备没有安装驱动,所以这一步我们用WinDDK sample----Toast来解决黄标问题!我的测试系统是Win10,安装驱动涉及到2个不起眼的方面:

1).需要为驱动包生成.cat文件;

2).为sys/cat签名。没有这2步,驱动没法安装。

2.1.修改Inf文件,使其匹配Bios虚拟的设备

我假设读者熟悉Wdm驱动,所以直接给出能解决黄标问题的inf文件内容:

[Version]
Signature="$CHICAGO$"
Class=System
ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318}
Provider=%TOS%
CatalogFile=demo.cat
DriverVer=02/23/2021,2.2.0.0[DestinationDirs]
demoNT.CopyFiles = 12[Manufacturer]
%HAN%=Demo,NTx86,NTia64,NTamd64[ControlFlags]
ExcludeFromSelect = *HAN0000[Demo.NTx86]
%*HAN0000.DeviceDescNTx86%=demo,*HAN0000[Demo.NTia64]
%*HAN0000.DeviceDescNTia64%=demo,*HAN0000[Demo.NTamd64]
%*HAN0000.DeviceDescNTamd64%=demo,*HAN0000[SourceDisksNames]
1=%demo%[SourceDisksFiles]
demo.SYS = 1[demo.NT]
CopyFiles=demoNT.CopyFiles[demo.NT.Services]
AddService=demo,2,demo_Service_Inst[demo_Service_Inst]
DisplayName    = %demo.SvcDesc%
ServiceType    = 1
StartType      = 0
ErrorControl   = 1
ServiceBinary  = %12%\demo.SYS[demoNT.CopyFiles]
demo.SYS[strings]
HAN                        = "HAN Co., Ltd."
demo                      = "demo"
*HAN0000.DeviceDescNTx86   = "Demo x86 ACPI-Compliant Value Added Logical and General Purpose Device"
*HAN0000.DeviceDescNTia64  = "Demo ia64 ACPI-Compliant Value Added Logical and General Purpose Device"
*HAN0000.DeviceDescNTamd64 = "Demo x64 ACPI-Compliant Value Added Logical and General Purpose Device"
demo.SvcDesc              = "Demo ACPI-Based Value Added Logical and General Purpose Device Driver"

2.2.根据Inf文件生成.cat

Inf文件指定要使用的.cat文件为:demo.cat,参考我的这篇博文:为sys/cat文件生成测试签名,即可生成demo.cat

Inf2cat.exe /driver:E:\WinDDk\my_test\objchk_win7_amd64\amd64\ /os:7_x64

2.3.为.cat/.sys签名

参考我的这篇博文:为sys/cat文件生成测试签名,可以生成证书文件并为sys/cat签名:

Signtool sign /a /v /s PrivateCertStore /n Contoso.com(Test) demo.cat
Signtool sign /a /v /s PrivateCertStore /n Contoso.com(Test) demo.sys

命令执行后,在.cat/.sys文件的文件属性页就有"Digital Signatures"标签页:

完成以上步骤,并在OS下开启Test Mode,之后在设备管理器里对"Unknow Device" 安装驱动程序,即可解决黄标问题:

ACPI\HAN0000从Other Device类移到了System Device类,另外,设备描述名也从Unknown Device变为Inf文件中DeviceDesc节指定的字符串:

3.编写驱动,接受ACPI Notification

这一节要分2部分:

1.虚拟设备HAN0000中转SCI到ACPI.sys;

2.驱动程序 处理ACPI Notify。

3.1.SCI的中转:进一步完善Bios Method(_L56)的功能,转发SCI到驱动层。

如开头所说,前一篇仅在Method(_L56)中打印日志,仅如此,OS无法获知来自PCH的SCI。参考ACPI Spec 5.6 ACPI Event Programming Model,ASL code还需要执行Notify语句,通知其他设备:

还是以ThinkPad的"Lenovo PM Device"为例,当设备(如EC)发现外部事件触发,同时又需要OS协同配合,此时设备会借道"Lenovo PM Device"这个Bios虚拟设备传话到OS(我怎么觉得它更像是阴阳界的灵媒?)

参考联想的做法,我可以在\_GPE.Method(_L56)中加入下列代码,从而将PCH SCI事件转交给\_SB.HAND。至于\_SB.HAND要做什么?它什么都不用做,躺平就行。因为它属于ACPI驱动,如果Bios层不做处理,驱动程序必须接管处理(官僚主义作风?),这不就是将控制权转移到软件层了?

External(\_SB.HAND, MethodObj)  Method (_L56, 0, Serialized){DSTR("l-event _L56");Notify(\_SB.HAND,0x9A)}

3.2.SCI的处理:完成ACPI驱动

ACPI驱动估计只有OEM厂商的驱动工程师会涉及,所以接下来的内容可能对大家有点陌生,但是我们背靠微软的ACPI driver Spec并结合WinDDK Toast,就能使工作量大大减轻。受限于篇幅,我仅列出用于实现接受ACPI Notification功能的代码。

3.2.1.获得ACPI direct call Interface接口并注册AcpiNotify:

NTSTATUS
DemoAddDevice(__in PDRIVER_OBJECT DriverObject,__in PDEVICE_OBJECT PhysicalDeviceObject)
{//Phase 1PDEVICE_OBJECT    fdo = NULL;PDEVICE_OBJECT  lowerDevice = NULL;PDemoDATA       demoData;RtlInitUnicodeString(&NameString, L"\\Device\\demo");status = IoCreateDevice (DriverObject,         // our driver objectsizeof (DEMODATA),      // device object extension size&NameString,         // FDOs do not have namesFILE_DEVICE_UNKNOWN,0,                     // No special characteristicsFALSE,&fdo);lowerDevice = IoAttachDeviceToDeviceStack(fdo,PhysicalDeviceObject);demoData= fdo->DeviceExtension;demoData->DeviceObject = fdo;demoData->LowerDeviceObject = lowerDevice;demoData->Pdo = PhysicalDeviceObject;//Phase 2status = GetAcpiInterfaces(demoData);RegisterNotifyHandler(demoData);KeInitializeSpinLock(&demoData->RetrieveLock);//Phase 3RtlInitUnicodeString(&DOSNameString, L"\\DosDevices\\demo");status = IoCreateSymbolicLink(&DOSNameString, &NameString);fdo->Flags &= ~DO_DEVICE_INITIALIZING;fdo->Flags |= DO_POWER_PAGABLE;
}

代码片中的Phase 1和Phase 3是常规的Wdm驱动框架;Phase 2则是获得ACPI direct call Interface并注册Notify函数。在微软的ACPI driver Spec<ACPI Driver Interface in Windows Vista>中对ACPI direct call Interface有详尽的介绍:

GetAcpiInterfaces用于获得ACPI direct call Interface:

extern ACPI_INTERFACE_STANDARD   AcpiInterfaces;NTSTATUS
GetAcpiInterfaces(IN DemoDATA demoData)
{KEVENT              event;IO_STATUS_BLOCK     ioStatus;NTSTATUS            status;PIRP                Irp;PIO_STACK_LOCATION  irpSp;KeInitializeEvent(&event, SynchronizationEvent, FALSE);Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,demoData->LowerDeviceObject,NULL,0,NULL,&event,&ioStatus);if (!Irp) {return(STATUS_INSUFFICIENT_RESOURCES);}Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;Irp->IoStatus.Information = 0;irpSp = IoGetNextIrpStackLocation(Irp);irpSp->MajorFunction = IRP_MJ_PNP;irpSp->MinorFunction = IRP_MN_QUERY_INTERFACE;irpSp->Parameters.QueryInterface.InterfaceType          = (LPGUID) &GUID_ACPI_INTERFACE_STANDARD;irpSp->Parameters.QueryInterface.Version                = 1;irpSp->Parameters.QueryInterface.Size                   = sizeof (AcpiInterfaces);irpSp->Parameters.QueryInterface.Interface              = (PINTERFACE) &AcpiInterfaces;irpSp->Parameters.QueryInterface.InterfaceSpecificData  = NULL;//// send the request down//status = IoCallDriver(demoData->LowerDeviceObject, Irp);return status;
}

AddDevice函数用返回的ACPI direct call Interface(存放在全局变量 ACPI_INTERFACE_STANDARD AcpiInterfaces中),调用RegisterNotifyHandler来注册ACPI Notification回调函数demoNotifyHandler :

NTSTATUS
RegisterNotifyHandler(IN PDemoData demoData)
{NTSTATUS   status;status = AcpiInterfaces.RegisterForDeviceNotifications(demoData->Pdo,demoNotifyHandler,AcpiInterfaces.Context);return status;
}

3.2.2.demoNotifyHandler处理来自Acpi层的Event

一般应用程序才是处理事件的终端,它会以异步等待的方式调用DeviceIoControl等待硬件SCI事件发生:

#define TVALDDRVR_DEVICE_OPEN_NAME   TEXT("\\\\.\\demo")hDriver = ::CreateFile(TVALZ_DEVICE_OPEN_NAME,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL |FILE_FLAG_OVERLAPPED ,NULL);::DeviceIoControl(m_hDriver, IOCTL_TVALZ_RETRIEVE_EVENT,&iaeInputBuffer, sizeof(iaeInputBuffer),&iaeOutputBuffer, sizeof(iaeOutputBuffer),&dwReturned, &ol);
if (::GetLastError() == ERROR_IO_PENDING)
{dwResult = ::WaitForMultipleObjects(2, hEventArray, FALSE, INFINITE) - WAIT_OBJECT_0;
}

1).当事件没有触发时,IRP进入驱动自定义的IRP Pending队列中。对应的应用程序则阻塞等待;

2).当ACPI Manager接收到来自Bios的SCI事件后,则调用注册的回调函数。此时,驱动程序遍历整个队列,比较队列中Pending IRP!AssociatedIrp.SystemBuffer的值是否和NotifyCode的值相同,如果相同则完成IRP。对应的应用程序被唤醒,进行下一轮Wait/Wake:

#define GPIO_QEVENT_L56                            0x9a //\_GPE._L56 发出的Notify Code的值为0x9A VOID
demoNotifyHandler(IN PVOID Context,IN ULONG ulNotifyValue)
{KIRQL              Irql;PULONG             pNotification;PLIST_RETRIEVE_IRP    pEntry, pNextEntry;PIOBUF_ACPI_EVENT    pAcpiEvent;UINT64             MsrData64 = 0; //AddNewEvent+WORK_QUEUE_ITEM*   turboWorkItem; //AddNewEvent+PIRP             retrieveIrp;DebugPrint(-1,(" :     NotifyHandler...Notification=%x\n", ulNotifyValue));KeAcquireSpinLock(&RetrieveLock, &Irql);if (!IsListEmpty((PLIST_ENTRY)&RetrieveList)) {for (pEntry = (PLIST_RETRIEVE_IRP)RetrieveList.ListEntry.Flink; pEntry != &RetrieveList; pEntry = pNextEntry) {pNextEntry = (PLIST_RETRIEVE_IRP)pEntry->ListEntry.Flink;pAcpiEvent = (PIOBUF_ACPI_EVENT)pEntry->pIrp->AssociatedIrp.SystemBuffer;if (pAcpiEvent->ulNotifyCode == ulNotifyValue) {RemoveEntryList((PLIST_ENTRY)pEntry);InsertTailList((PLIST_ENTRY)&CompleteList, (PLIST_ENTRY)pEntry);if(ulNotifyValue == GPIO_QEVENT_L56){DebugInfo(1,("Demo:receive Q-event _L56\n"));}else{}
//tvalz_AddNewEvent+                   }}}KeReleaseSpinLock(&RetrieveLock, Irql);if (!IsListEmpty((PLIST_ENTRY)&CompleteList)) {for (pEntry = (PLIST_RETRIEVE_IRP)CompleteList.ListEntry.Flink; pEntry != &CompleteList; pEntry = pNextEntry) {pNextEntry = (PLIST_RETRIEVE_IRP)pEntry->ListEntry.Flink;RemoveEntryList((PLIST_ENTRY)pEntry);IoSetCancelRoutine(pEntry->pIrp, NULL);pAcpiEvent = (PIOBUF_ACPI_EVENT)pEntry->pIrp->AssociatedIrp.SystemBuffer;pAcpiEvent->dwStatus = STATUS_SUCCESS;pEntry->pIrp->IoStatus.Information = sizeof(PIOBUF_ACPI_EVENT);pEntry->pIrp->IoStatus.Status = STATUS_SUCCESS;IoCompleteRequest(pEntry->pIrp, IO_NO_INCREMENT);}}TvalzDebug(Tvalz_INFO,("TVALZ : NotifyHandler...end\n"));
}

由于应用程序等待的NotifyCode就是Bios code中Method (_L56) Notify语句抛出的值,因此只要GPIO引脚没接地,回调函数会一直被调用,因此DbgView界面上会输出大量的日志

附注:为了Enable DbgView输出:

1).在注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager下创建DWORD子项"Debug Print Filter",设置其值为0xF;

2).在DbgView中勾选"Enable Verbose Kernel Output"

结尾

本文完。还有后续文章吗?当然有!驱动程序有部分工作是处理电源\Pnp事件,这些也需要Bios协同配合,所以下一篇是Bios和驱动如何处理电源事件~请期待

[原创]ACPI.sys,从Windows到Bios的桥梁(2):Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇相关推荐

  1. 从Windows到Bios的桥梁:Windbg跟踪Win7开启ACPI

    点击上方蓝色"终端研发部",选择"设为星标" 学最好的别人,做最好的我们 本文为看雪论坛优秀文章 看雪论坛作者ID:hyjxiaobia 由于windows设备 ...

  2. [原创].使用Nios II 9.1中的Flash Programmer无法固化程序到EPCS上

    情况描述 自从装了91,就发现通过NII的Flash Programmer编程后,上电无法正常复位,也就是说无法固化程序到EPCS中(或其他Flash器件).本来我以为是我的EPCS出问题了,但是在Q ...

  3. 电脑缺失ACPI.sys

    解决方法: 注:U盘内需要有PE系统 拷贝一份正常的acpi.sys文件到u盘,插入电脑 > 开机时按F9 > 选中第三个USB *** > 回车进入PE系统,将acpi.sys放入 ...

  4. BIOS + GPT + GRUB + Linux + Windows 折腾笔记

    其实从标题就能看出来我有多蛋疼了.我不期望还有别的人和我有同样的奇怪需求,但是希望本文的一部分或几部分能对部分折腾者有一定有作用. 一.为什么会有这样的需求 要 BIOS 不要 UEFI 虽说现在的主 ...

  5. 在ThinkPad W500 A98上升级Windows 7以及安装硬件驱动和相关程序(2/2)

    在ThinkPad W500 A98上升级Windows 7以及安装硬件驱动和相关程序(2/2) 升级硬件固件 在安装了升级硬件固件所必要的驱动程序后,就可以升级硬件固件了,在下载的硬件驱动和相关程序 ...

  6. 在ThinkPad W500 A98上升级Windows 7以及安装硬件驱动和相关程序

    在ThinkPad W500 A98上升级Windows 7以及安装硬件驱动和相关程序 备份数据 先备份数据,由于原来的Windows XP已经不能正常启动,因此就使用光驱启动Windows XP P ...

  7. Windows PE入门基础知识:Windows PE的作用、命名规则、启动方式、启动原理

    Windows PE的全名是WindowsPreinstallationEnvironment(WinPE)直接从字面上翻译就 是"Windows预安装环境".微软的本意是:Win ...

  8. 加快windows上对大文件,以及很多很多小文件进行不同磁盘拷贝的速度——windows上的最快拷贝软件FastCopy

    欢迎大家关注笔者,你的关注是我持续更博的最大动力 原创文章,转载告知,盗版必究 加快windows上对大文件,以及很多很多小文件进行不同磁盘拷贝的速度--windows上的最快拷贝软件FastCopy ...

  9. linux添加windows网络打印机,Linux Mint如何添加windows分享的网络打印机?

    1.安装samba sudo apt-get install samba 2.找到系统打印机选项 通过 Menu-->>控制中心-->>系统管理找到 Printers选项,双击 ...

最新文章

  1. java实现遍历树形菜单方法——Dao层
  2. ffmpeg开发指南(使用 libavformat 和 libavcodec)
  3. Spring @PostConstruce 和 @PreDestroy 实例化\销毁 bean 时
  4. (24)System Verilog设计十进制计数器
  5. PyTorch 1.0 中文文档:torchvision.models
  6. 从其他文件夹导入文件
  7. siki暗黑战神项目总结,框架和主要的优化点
  8. weblogic启动项目失败查看_weblogic 部署项目成功,但是再界面上取不到数据
  9. 直击备份恢复的痛点:基于 TiDB Binlog 的快速时间点恢复
  10. MVC中局部视图的使用
  11. 宁皓网 react native 视频教程 ECMAScript6
  12. C++基础编程题(27)输入一个数字,为其高,一个符号*,输出该符号组成的平行四边形形状
  13. 软件工程第三次作业(微软小娜案例分析)
  14. 幸运数 c++程序(详解,附完整代码)
  15. 商标注册需要多久下证
  16. 环境混合物总体效应:加权分位数和回归(WQS)
  17. 武汉php工程师,Laravel 招聘:[武汉][10-20k][光谷][CmsTop新项目]高级PHP工程师 | Laravel China 社区...
  18. 硬件nat关闭还是开启_「Windows」到底要不要开启“快速启动”,有没有副作用?...
  19. c语言中set 函数,C里边的STL里边的Set函数
  20. requests-爬取美女图片源码

热门文章

  1. sql学习-with as的使用-分析数据得到结果
  2. 1168 输出连续的整数序列 之二
  3. Java注解和反射,Java教程马士兵全集
  4. “书香校园”读书知识竞赛试题(四)
  5. matlab万花,万花尺的动态模拟程序
  6. 计算机组成原理——计算机系统的层次结构
  7. python前端基础知识总结 及部分练习题
  8. 星际入门—各族开局解析
  9. Shell实现命令先后执行
  10. 台式计算机用手机流量上网,手机流量怎么给台式电脑用