Note: SMBUS on Windows

根據網路上一些文章看來, Microsoft 最後放棄提供一般化的SMBUS 的interface 給driver or Application使用, 最早在1999年Microsoft 有提供過WDM DDK的Sample code, 而且SMBUS協會當時有提供過一個 SMBus Device Driver External Architecture Specification, version 1.0做一個規格與說明文件, 但是之後不了了知, 這個SMBUS一般用來提供 DRAM的SPD 與Smart Batteries Gauge  的連接, 當初定義了GUID_SMB &  FILE_DEVICE_SMB, 當作 device driver Interface 與 IO_CTL_CODE 用途, 不過後來因為種種因素都只支援ASL的operations, 但是一般來說除非有Battery 接在SMBUS Bus上面系統會透過ASL code 去access 外, 一般來說不會有任何存取動作, 變成可以直接 I/O port access devices.

目前遇到Linux 因為有標準的ACPI EC-SMBHC driver 變成想轉成這個模式, 但是這個模式在Windows 下看起來是沒有直接支援的方法, 應該還是試著I/O port access 畢竟不是Laptop 產品沒有掛電池在上面,遇到掛電池的狀況在來想法子好了, 估計只能寫 vendor specific acpi driver 去搭配ASL處理了

ref.

廣告

Note: How to testing a Wintel Modern PC on Manufacturer side

這一兩週因為一些需求, 對WinPE, Sysprep, DISM, 等等東西有了一些基本了解, 對於該怎樣快速的生產Wintel PC 也確定了一些方法, 現在影響PC生產的問題都是因為已經沒有DOS, 而且週邊裝置也不像以前那樣單純BIOS可以負責絕大多數的設備操作, 可以寫些很簡單的程式就可以進行周邊測試, 如果設備本身可以跑Linux而且包含全部Device, 用Linux當生產驗證實際上是個不錯的主意, 如果有Ethernet 甚至可以做PXE boot 自動下載測試程式, 然後搭配合適的測試治具, 可以作到幾乎全自動化的組裝後驗證測試, 可以節省大量人工操作與提昇測試速度

但是事情往往不是那樣簡單, 現在還是很多設備只有Windows driver & Factory mode, 基於裝置廠商的商業保密需求, 目前看起來WinPE還是一個最好的選擇可以簡單的安裝在USB Disk 上面, 生產時可以直接開機, (BIOS 可以做一個生產模式版本, 預設USB bootable), WinPE開機基於RAM DISK, 基本上API都可以用Driver 也可以動, 而且是在做image就可以加入的設計, 也一樣可以透過PXE遠端boot, 生產時自動執行相關測試程式完成後對裝置燒入出貨版本的BIOS與OS image 即可, 當然上面都需要養一兩個人做這些工作, 而且不同的設備可能需要重新re-configuration, 不過當設備大量生產時可以大量的節省成本這包含人物力與時間。

  1. prepare WinPE environments to add all relations driver for reference device
  2. to create a test application for verify interface/burn-in test and install  OS image. etc. should to include all test items.
  3. add customer script to auto launch the test application
  4. install the customization WinPE to USB flash disk or another bootable device.
  5. Plug the device on DUT, to Test device and install OS image by automation, will reduce most time on manual operation. so should to use fastest disk

Windows 安裝有下面幾個模式, 我所知道的是從XP就有, 更早之前懶的考了

OOBE, (Out of Box Experience), 中文翻譯成系統全新體驗, 基本上就是一般拿到Microsoft 的系統安裝檔案, 開機的安裝畫面下就是處於這個模式, 此時可以按 Ctrl+Shift+F3 讓 系統進入下一個模式
Audit, 這中文稱作審核模式, 它會直接進入Administrator 管理者帳號開機, 然後讓你安裝各種應用程式與驅動程式, 安裝好後一般透過Sysprep 去做一般化(generalize)讓你移除所有已經安裝的Driver, 系統帳號與安全辨識訊息設定, 這裡的移除Driver不是移除檔案, 是移除registry 內安裝掛載的資訊, 而檔案本身是保留在Windows 目錄下, 所以也可以作為當更新主機板時, 不想全部重灌系統時, 但是因為一般化會移掉使用者資訊與檔案記得備份或是另外設定跳過移除相關目錄

sysprep 預設有執行次數限制  透過底下方法可以Reset (https://www.symantec.com/connect/articles/how-sysprep-windows-vista-7-or-8-image-more-three-times)

1) Open up Registry Editor. (Start > Run > regedit)

2) Browse to HKEY_LOCAL_MACHINE\System\Setup\Status\Sysprep Status.

3) Change the value of CleanupState to 2. (0x00000002)

4) Change the value of GeneralizationState to 7. (0x00000007)

5) Browse to HKEY_LOCAL_MACHINE\Software\Microsoft\WindowsNT\CurrentVersion\SoftwareProtectionPlatform.

6) Change the value of SkipRearm to 1. (0x00000001)

7) Open an administrative command prompt. (Start > Run > cmd… right click on the cmd icon and go to Run as Administrator)

8) Type msdtc -uninstall and push enter.  Wait a minute and reboot.

9) Type msdtc -install and push enter.  Wait a minute and reboot.

10) Browse to c:\windows\system32\sysprep.  Delete the panther folder.

11) Run sysprep.  It should now complete, and you can capture the image.

Note: Access SMBIOS in Kernel of Windows

因為老闆說要給部門做一個簡單的talk, 鑑於目前同事多數是 Junior level, 而公司從事的還是x86為主的硬體生意, 所以決定中秋過完的部門週會上, 簡單介紹一下SMBIOS 的來由與存取方法, 讀資料過程還是有新的收穫, 學到如何kernel 使用wmi

開始做一些研究, 找到一些舊的資料, 基本上SMBIOS 真的開始全面導入是 v2.3.1之後, Win95 就開始使用PNPBIOS 界面提供 SMBIOS的支援, 但是直到 1999年Microsoft 才開始加入 Design for Windows Logo 認證裡面(from PC 99 Design guide), 不過強制需求應該是從Windows XP SP2/Windows 2003 Server SP1 開始引入了mssmbios.sys, 在WinHEC 2005有Release了一個文件 smbios.doc

該文件解釋了mssmbios.sys的一些行為,  基本上是基於WMI機制等於內部做了MOF宣告, 然後會去parsing smbios table資料, 並且存在 registry path 下"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Mssmbios\Data"

因為基於WMI, 所以它提供XP SP2與之後版本的user/kernel mode 的 wmi 存取, 為了更簡便使用user mode 還透過增加GetSystemFirmwareTable() API (from Windows 2003 server sp1), 而kernel mode 則從Vista 開始 多了 AuxKlibGetSystemFirmwareTable

kernel wmi 則是透過 IoWmiOpenBlock(), IoWMIQueryAllData(), “SMBIOS_DATA_GUID" 去取得, 這還要看一下 driver load order, 否則貿然在kernel driver使用應該會出問題

Note: Some C# code snippets

最近工作內容牽扯 customization Windows Image, 或是需要緊急處理狀況, 都在用C sharp 多, 有時候發現內建.Net 有大量System libraries 還蠻方便的

installProductKey 這個實際上是參考 slmgr.vbs 去找出它如何 Install Product Key of Windows, 至於 getBaseBoardManufacturer 就是透過WMI去讀SMBIOS Data, 是有遇過BIOS 沒填好SMBIOS的狀況

static void installProductKey(string key)
{
    var mbs = new ManagementObjectSearcher("Select Version From SoftwareLicensingService");
            
    foreach (ManagementObject mo in mbs.Get())
    {
        mo.InvokeMethod("InstallProductKey", new object[] { key });
    }

}

static public string getBaseBoardManufacturer()
{
    string ret = "Unknow";
    try
    {
        var mbs = new ManagementObjectSearcher("Select Manufacturer From Win32_BaseBoard");
        foreach (ManagementObject mo in mbs.Get())
        {
            ret = mo["Manufacturer"].ToString();
        }
    }
    catch (Exception)
    {
        ret = "Unknow"; // Normally don't have SMBIOS information
    }
    return ret;
}

snippets on github

Note: using SetupAPI to find a special device on Windows system,

// EnumHIDDevice.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#pragma comment(lib, "setupapi")

bool chkDigitizer(void)
{
    HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
    bool bFind = false;
    hDevInfo = SetupDiGetClassDevs(
        &GUID_DEVCLASS_HIDCLASS,
        NULL,
        NULL,
        DIGCF_PRESENT);
#if 0
    // get special device instance
    hDevInfo = SetupDiGetClassDevs(
        &GUID_DEVCLASS_HIDCLASS,
        TEXT("HID\\VID_0458&PID_501E&COL05\\7&198536FE&0&0004"),
        NULL,
        DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
#endif

    if (INVALID_HANDLE_VALUE == hDevInfo)
        return false;

    SP_DEVINFO_DATA DeviceInfoData;
    ULONG DeviceIndex;

    ZeroMemory(&DeviceInfoData, sizeof(SP_DEVINFO_DATA));
    DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
    DeviceIndex = 0;
    while (SetupDiEnumDeviceInfo(hDevInfo, DeviceIndex, &DeviceInfoData))
    {
        DeviceIndex++;

        TCHAR InstanceID[MAX_PATH + 1];
        DWORD reqSize = 0;
        TCHAR CheckLists[][MAX_PATH + 1] = {
            TEXT("HID\\VID_0458&PID_5020&COL05"), // new
            TEXT("HID\\VID_0458&PID_501E&COL05"), // older
        };

        SetupDiGetDeviceInstanceId(hDevInfo, &DeviceInfoData, InstanceID, sizeof(InstanceID), &reqSize);
        for (ULONG i = 0; i < 2; i++)
            if (NULL != _tcsstr(InstanceID, CheckLists[i]))
            {
                bFind = true;
                break;
            }
    }
    if (INVALID_HANDLE_VALUE != hDevInfo)
        SetupDiDestroyDeviceInfoList(hDevInfo);

    return bFind;
}

int _tmain(int argc, _TCHAR* argv[])
{

    if (chkDigitizer())
        _tprintf(TEXT("Find out a digitizer device on system\n"));
    else
        _tprintf(TEXT("no digitizer on system\n"));

	return 0;
}

Note: create a named EVENT for share with a correct access permission on Win 7 & later

最近遇到某個工作項目需要寫個Service 來與一般的應用程式互動, 本來想用Pipe or File mapping, 做IPC
但是實際開始工作後發現 他們之間透過Event 就可以處理,
但是Windows 從Vista 開始引入UAC後 開始加強系統各種Security(可以想成linux enabled SELinux)
對各種Windows 內的 Object 都有了權限要求, 因此建立Object實際要指定好權限
這篇簡單的SDDL 產生一個 kernel object 的權限片斷程式碼,
要觀察建立好的Kernel object 可以直接透過 Process Explorer開Low Panel並且指定Handle 這樣就可以

    wchar_t  myEventName[] = L"Global\\MyEventTest";
    SECURITY_ATTRIBUTES sa = { 0 };
    PSECURITY_DESCRIPTOR pSd = NULL;
    ConvertStringSecurityDescriptorToSecurityDescriptor(
	L"D:"			// Discretionary ACL
				// ref. http://msdn.microsoft.com/en-us/library/windows/desktop/ms686670(v=vs.85).aspx
	L"(A;;0x001F0003;;;BA)"	// EVENT_FULL_ACCESS, Built-in Administrators
	L"(A;;0x00100002;;;AU)",// SYNCHRONIZE & MODIFY_EVENT_STATE, Authenticated users
	SDDL_REVISION_1, &pSd, NULL);
	sa.nLength = sizeof(sa);
	sa.bInheritHandle = FALSE;
	sa.lpSecurityDescriptor = pSd;

	// create a auto-reset event for reload configuration
    m_hSignals[EVENT_RELOAD_CONF] = CreateEvent(&sa, FALSE, FALSE, myEventName);
    if (m_hSignals[EVENT_RELOAD_CONF] == NULL)
    {
	throw GetLastError();
    }
    LocalFree(pSd); // release 

ref. http://stackoverflow.com/questions/19049412/how-to-add-synchronisation-right-in-a-sddl-string-for-createevent