Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

admin 2021年3月10日10:00:29评论283 views字数 9026阅读30分5秒阅读模式
Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

本文为看雪论坛精华文章

看雪论坛作者ID:hyjxiaobia




在上一篇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)。

以下是实现该目标的步骤:

Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

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

Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇


设备驱动程序必然依存于某类设备(DeviceNode),这类设备或真实存在,或虚拟创建。虽然本段的标题写的是创建虚拟设备,但该虚拟设备不同于Winddk Sample中Toast创建的设备----Toast通过IoCreateDevice创建,而是由Bios在ACPI命名空间中创建的设备,希望读者加以区分。

那么这样的虚拟设备该怎么创建?ACPI Spec 5.4节有现成可用的例子!很多OEM厂商为了实现硬件增值功能就会创建这种虚拟设备,比如联想(联想打钱!)ThinkPad的"Lenovo PM Device"就属于此。我们可以借助RW utility查看它的实现:

Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇
Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

嗯,大家可以参考ThinkPad,依葫芦画瓢可以写出这样的ASL code,编译进Bios开机过后,就能在设备管理器里找到带黄标的“Unknown Device”:
Scope(_SB) {    Device(HAND) { #HAND->Han's device        Name(_HID, EISAID("HAN0000"))          Method(_STA) {                Return(0x0B)        }    }}

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

Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇
Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇



Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

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

Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

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

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

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

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

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


我假设读者熟悉Wdm驱动,所以直接给出能解决黄标问题的inf文件内容:
[Version]Signature="$CHICAGO$"Class=SystemClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318}Provider=%TOS%CatalogFile=demo.catDriverVer=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    = 1StartType      = 0ErrorControl   = 1ServiceBinary  = %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文件生成测试签名(https://blog.csdn.net/lixiangminghate/article/details/52567035,即可生成demo.cat
Inf2cat.exe /driver:E:WinDDkmy_testobjchk_win7_amd64amd64 /os:7_x64


2.3 为.cat/.sys签名


参考我的这篇博文:为sys/cat文件生成测试签名(https://blog.csdn.net/lixiangminghate/article/details/52567035,可以生成证书文件并为sys/cat签名:
Signtool sign /a /v /s PrivateCertStore /n Contoso.com(Test) demo.catSigntool sign /a /v /s PrivateCertStore /n Contoso.com(Test) demo.sys

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

Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

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

Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

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

Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇



Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

Step 3. 编写驱动,接受ACPI Notification

Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

这一节要分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 ModelASL code还需要执行Notify语句,通知其他设备:

Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

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

Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

参考联想的做法,我可以在_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:

NTSTATUSDemoAddDevice(__in PDRIVER_OBJECT DriverObject,               __in PDEVICE_OBJECT PhysicalDeviceObject                ){    //Phase 1    PDEVICE_OBJECT  fdo = NULL;    PDEVICE_OBJECT  lowerDevice = NULL;    PDemoDATA       demoData;         RtlInitUnicodeString(&NameString, L"\Device\demo");    status = IoCreateDevice (                    DriverObject,           // our driver object                    sizeof (DEMODATA),     // device object extension size                    &NameString,            // FDOs do not have names                    FILE_DEVICE_UNKNOWN,                    0,                      // No special characteristics                    FALSE,                    &fdo);      lowerDevice = IoAttachDeviceToDeviceStack(fdo,PhysicalDeviceObject);    demoData= fdo->DeviceExtension;    demoData->DeviceObject = fdo;    demoData->LowerDeviceObject = lowerDevice;    demoData->Pdo = PhysicalDeviceObject;      //Phase 2    status = GetAcpiInterfaces(demoData);    RegisterNotifyHandler(demoData);    KeInitializeSpinLock(&demoData->RetrieveLock);      //Phase 3    RtlInitUnicodeString(&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有详尽的介绍:

Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇
GetAcpiInterfaces用于获得ACPI direct call Interface:
extern ACPI_INTERFACE_STANDARD AcpiInterfaces;  NTSTATUSGetAcpiInterfaces(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 :
NTSTATUSRegisterNotifyHandler(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   VOIDdemoNotifyHandler(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=%xn", 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 _L56n"));                    }                    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...endn"));}

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

Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

附注:为了Enable DbgView输出:


1) 在注册表HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlSession Manager下创建DWORD子项“Debug Print Filter”,设置其值为0xF;

2) 在DbgView中勾选“Enable Verbose Kernel Output”


Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

结尾

Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇


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



Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

- End -



Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇


看雪ID:hyjxiaobia

https://bbs.pediy.com/user-home-399589.htm

  *本文由看雪论坛 hyjxiaobia 原创,转载请注明来自看雪社区。




# 往期推荐





Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇
公众号ID:ikanxue
官方微博:看雪安全
商务合作:[email protected]



Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

球分享

Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

球点赞

Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

球在看



Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

点击“阅读原文”,了解更多!

本文始发于微信公众号(看雪学院):Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年3月10日10:00:29
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Windows应用程序响应主板上GPIO(SCI)设备中断 软件篇https://cn-sec.com/archives/285739.html

发表评论

匿名网友 填写信息