小華的部落格: 2014/10/19 - 2014/10/26

bloggerads_Head

搜尋此網誌

星期四, 10月 23, 2014

WMI ACPI

今天要整理的筆記是WMI ACPI
       
     在BIOS工程師中,對於WMI ACPI應該大家都有玩過,因為許多的OEM都是開發一些OS端的應用程式,然後再透過WMI 這種方式去跟BIOS的ACPI ASL CODE做溝通,以達到可以讀取Thermal 資訊/EC 資訊/ BIOS Setup Utility 資訊....etc.

   
在網路上或是微軟的相關文件中,你可以看到主要的架構在於wmiacpi.sys 跟acpi.sys 之間的溝通,對於BIOS工程師來說,如果要去提供這種介面就不得不參考微軟的wmi acpi白皮書上面介紹的內容了,底下是微軟文件的說明:

http://msdn.microsoft.com/en-us/library/windows/hardware/dn614028(v=vs.85).aspx

這份文件中主要的概念有幾個:

1. 他跟你說有一個MOF(Managed Object Format) 是用來給 WMI Core使用的,所以他有個資料庫管理這些MOF檔案;當上層的APP跟WMI Core溝通時,他要去這個資料庫撈對應的MOF資訊,從MOF資訊中取得一些資訊後,才知道怎麼跟你的ACPI溝通!
2.溝通的那個ACPI Device有一個特別的PNPID,也就是PNP0C14 ! 所以BIOS工程師要宣告一個ACPI Device然後底下定義_HID , "PNP0C14" 這樣子才能夠溝通~
3.Wmi Core 溝通的方式就請自行看這篇文章,像是如何透過GUID找到ACPI _WDG內比對GUID然後找到兩個字元後,再去看Flag屬性是否是Method,然後去執行相關的ACPI Method...之類的資訊,因為這些資訊已經在網路上很多,大家也應該熟悉了,這邊就不多描述。 底下是對_WDG對應時的C語言格式:

typedef struct
{
     GUID guid;             // GUID that names data block
     union
     {
         CHAR ObjectId[2];  // 2-character ACPI ID  (Data Blocks and Methods)
         struct 
         {
             UCHAR NotificationValue;  // Byte value passed by event handler control method
             UCHAR Reserved[1];
         } NotifyId;
    }
     USHORT InstanceCount;  // Number of separate instances of data block
     USHORT Flags;          // Flags
};
上面的C結構會對應倒ASL的_WDG 內的定義:
   Name(_WDG, Buffer() {
        0x6a, 0x0f, 0xBC,....//GUID
        66, 65,              // Object ID (BA) (兩個字元"BA",可能會組成WQBA或是WMBA,看Flag)
        3,                   // Instance Count (有幾筆Data items)
        0x01,                // Flags (決定他是不是Method...之類的屬性來決定WQXX/WMXX...etc)
        
    }) 


那大家可能會問說,你不說運作原理那你要說甚麼?

一般BIOS工程師在Debug時一般就是利用這些運作原理去追ACPI 來找出問題點,一般大家會比較忽略說,那上層的MOF是怎麼加進去資料庫的? 所以這邊會對這部分做一個簡單的說明。在說明之前,我們先看看下面這張圖:


Vbscript :    代表的就是上層APP可以透過像是VBScript/C#....etc之類的去開發你的APP。
Wmi Core :  會去撈MOF 資訊跟Acpi.sys溝通(透過WmiAcpi.sys) 。
ACPI ASL :  你要宣告PNP0C14 ,然後有個Name Object 叫做 _WDG (上面圖畫錯了,懶得改了)。
BIOS Function: 然後透過ACPI ASL的相關Method來實做一些BIOS CODE,像是發個SMI去取得一些資訊,再透過WMI_ACPI ASL往回傳給上層APP...etc.。


Installation wmi acpi方式有兩種:

方式1: MOF包在DLL,並註冊到Registry
1. 增加上述說的那些ASL CODE到你的BIOS

2. 建立一個MOF.DLL (類似OS動態連結檔,不過裡面沒有任何OBJ只有MOF資料)
    a. 建立wmiacpi.rc 跟wmiacpi.def 檔案
    b. 透過VS2010/2012/2013 的link.exe 將他變成wmiacpi.dll 裡面沒有obj,只有 MOF資料

wmiacpi.rc 內容:

#include <windows.h>
#include <ntverp.h>

#define VER_FILETYPE            VFT_DLL
#define VER_FILESUBTYPE         VFT_UNKNOWN
#define VER_FILEDESCRIPTION_STR         "Resource only DLL containing MOF for ASL code"
#define VER_INTERNALNAME_STR            "wmimof"
#define VER_ORIGINALFILENAME_STR        "wmimof.DLL"

#include "common.ver"

//
// WMIACPI.SYS requires that the mof resource be named MofResource

MofResource MOFDATA wmiacpi.bmf


wmiacpi.def 的內容就只有一行: 
LIBRARY wmiacpi.dll

    c. link command : 

rc /fo"%OUTDIR%\wmiacpi.res" wmiacpi.rc

link /OUT:"%OUTDIR%\wmiacpi.dll" /NOLOGO /DLL "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" 

"ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /DEF:"wmiacpi.def" %OUTDIR%\wmiacpi.res /MANIFEST /ManifestFile:"%OUTDIR%

\wmiacpi.dll.intermediate.manifest" /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /PDB:"%OUTDIR%\wmiacpi.pdb" /TLBID:1 /NOENTRY /DYNAMICBASE 

/NXCOMPAT /MACHINE:X86 /ERRORREPORT:QUEUE


3. 複製MOF.DLL檔案到%windir%\system32 (也就是C:\Windows\System32)

4. 在Registry 的HKEY_LOCAL_MACHINE\CurrentControlSet\Services\WmiAcpi底下新增MofImagePath,然後裡面的值指向 "C:\Windows\System32\MOF.DLL"
5. 重新開機,然後OS偵測到PNP0C14之後就會自動安裝WmiAcpi.sys 然後會去Registry 找到你包在MOF.DLL內的資料,之後用你的APP來透過WMI Core去存取你的ACPI ASL.


※ 這邊說的MOF.DLL就是你透過link.exe 建立出來的檔案,請自行變更你的名稱

方式2: MOF包在ACPI ASL,等發送特定IRP時回給Wmi Core.

1. 增加上述說的那些ASL CODE到你的BIOS

2. 建立一個MOF.BMF (MOF的Binary格式,副檔名隨便給因為你可以在Compiler時指定副檔名)

3. 透過 wmimofck.exe 產生.x 檔案,在把他的內容包到ACPI ASL之中.

a. 首先先在_WDG內定義一個專屬MOF的GUID(切記不能改變GUID值) 

   Name(_WDG, Buffer() { 
       .
       // This GUID for returning the MOF data
       0x21, 0x12, 0x90, 0x05, 0x66, 0xd5, 0xd1, 0x11, 0xb2, 0xf0, 0x00, 0xa0, 0xc9, 0x06, 0x29, 0x10, //
       66, 65,        // Object ID (BA)  這兩個字元可以自己改,但是要對應到WQXX.
       1,                 // Instance Count 
       0x00,          // Flags    
   })

b.將Wmiacpi.x 內容複製貼上到Name Object "WQBA"

  Name(WQBA), Buffer(){"MOF Binary data from Wmiacpi.x"}

4. 重新開機,然後OS偵測到PNP0C14之後就會自動安裝WmiAcpi.sys 然後會發送特定的IRP,然後ACPI那邊會回給WMI CORE MOF的資訊~

在這兩中方式中,都需要把wmiacpi.mof 檔案先經過一個工具叫做mofcomp.exe compiler
而這個wmiacpi.mof 檔案的內容就是你在微軟文件那邊看到的,例如:

class WMIEvent : __ExtrinsicEvent
{
};


[WMI,
 Locale("MS\\0x409"),
 Description("This class contains the definition of the package used in other classes"),
 guid("{ABBC0F60-8EA1-11d1-00A0-C90629100000}")
]
class Package
{
    [WmiDataId(1),
     read, write,
     Description("16 bytes of data")
    ] uint8 Bytes[16];

};
...

然後經過mofcomp.exe compiler之後,會產生一個MOF binary 檔案,我把他叫做wmiacpi.bmf。

而這個wmiacpi.bmf 可以經過另一個工具wmimofck.exe 來產生c file / c header file ,如果你想自己寫一個Wmi driver時,可以透過這種方式把來幫助你產生一些需要的程式碼,也可以透過wmimofck.exe來產生VBScript 的Sample code(自動產生),所以當然也可以透過wmimofck.exe來產生一堆HEX 文字檔來方便你把MOF binary資料包進去ACPI ASL之中。

底下是我的批次檔,用來測試用的,分享給大家參考~






※ 這邊使用到的資訊都是來自於微軟的相關文件,有興趣請自行查詢!

需要用到的工具:
Windows WDK內的wmimofchk.exe (我是安裝WDK for win7 ,目前最新版是WDK 8.1)

Reference 

http://msdn.microsoft.com/en-us/library/windows/hardware/dn614028(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/ff542012(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/hardware/ff554794(v=vs.85).aspx