Note: SW SMI in Kernel driver

在研究Windows 上要做一個SW_SMI的操作, 要先解決一個Buffer交換的問題, 看起來只有下面這個方法可以配置對應的Memory, 讓SMI emitter 去配置然後給SMM Handler 使用的樣子

MmAllocatePagesForMdlEx

想法是利用一個Driver 透過IOCTL 讓User Application去發起動作, 然後SMI Handler 需要透過Register 收到Buffer Address, 分別有32bits/64Bits 的不同
Windows 下面Driver 透過MmAllocatePagesFormMdlEx 去配置一個連續的記憶體區塊, 可能4K~64K就足夠一般使用, 或是1M? for BIOS update?

廣告

Touch Driver Architecture change on Windows Embedded Compact 7

這幾天又開始轉弄WINCE OS 的porting, 這次的任務是在 CedarTrail 上要 跑 WEC7, BSP 還是使用 Adeneo Embedded 所提供的, 根據前兩年做過的經驗, 很快就 Booting 成功了, Azalia Audio codec 這次換成 ALC269, 也稍微改一下就正常工作了,

但本來不預期會出意外的Touch Controller 卻不會動, 經過一早努力才發現, 原來 WEC7 把Touch Driver 的結構修改過了, 從以前單純的 PDD/MDD 結構, 又更一步把介面抽象化, 分成了 Touch Proxy 與 Touch Driver, Touch Driver 現在改成標準的 Steram Driver(但是還維持PDD/MDD結構) 更上層與系統GWES介面的改成單純由 Touch Proxy Driver 負責. 詳細的內容請參考MSDN 上的 Touch Driver Architecture 因應這個修改 Registry 需相應修改

WorkNote: a Debug message dump, Intel N450 with WEC7 Platform

Keypoint:
1. ACPI provide RAM size to CE kernel.
2. Due to Original code just discover ‘zero’ codec, so the InitHDA() always return failed. And the DiscoverCodec() still have a little bug,  so you need to patch it! May be still need added your codec ID into CheckcodecFG().

Debug Serial Init

SysInit: GDTBase=82acb000 IDTBase=82acf700 KData=82a85800
Windows CE Kernel for i486
INFO:OALLogSetZones: dpCurSettings.ulZoneMask: 0xb
PCIInitConfigMechanism():ucConfigMechanism 0x1
InitKitlNIC: Searching for PCI Ethernet NIC (dwIrq = 0, dwIoBase = 0, dwDfltType = 0) …
Intel(R) Ethernet Gigabit KITL driver [Build: Apr  9 2012  14:34:00]
InitKitlNIC: skipping unknown PCI Ethernet NIC: (subclass=0, Vendor=10EC, Device=8168)
Intel(R) Ethernet Gigabit KITL driver [Build: Apr  9 2012  14:34:00]
InitKitlNIC: skipping unknown PCI Ethernet NIC: (subclass=0, Vendor=10EC, Device=8168)
Intel(R) Ethernet Gigabit KITL driver [Build: Apr  9 2012  14:34:00]
InitKitlNIC: skipping unknown PCI Ethernet NIC: (subclass=0, Vendor=10EC, Device=8168)
Intel(R) Ethernet Gigabit KITL driver [Build: Apr  9 2012  14:34:00]
InitKitlNIC: skipping unknown PCI Ethernet NIC: (subclass=0, Vendor=10EC, Device=8168)
 x86InitMemory(): dwAcpiPHYS = 0x3F5CD627
x86InitMemory(): dwRamEndPHYS = 0x20000000
x86InitMemory(): dwRamTop(1)= 0xc0000000
Using ACPI location to determine RAM size
ACPI Tables found at 0x3f5cd627
RAM reported to kernel 1013MB
x86InitMemory():g_pOemGlobal->dwMainMemoryEndAddress = 0xa0000000
Found ACPI but it was located above 512MB at 1013MBIDE deviceId = 0x2828

disLegacyUsbSupport LPC deviceId=0x2815 PMBase=0x1000
+ disLegacyUsbSupport for ICH8M
– disLegacyUsbSupport for ICH8M

PID:00400002 TID:00410002  Found ACPI but it was located above 512MB at 1013MBReserve KITL IRQ: No IRQ reserved, KITL polling mode was specified
PID:00400002 TID:00510002 WARNING: COM1: has been reserved exclusively for Debug Messages.VBridge:: VB_INITIALIZED returns [0]
PID:00400002 TID:00A40006 HDA::InitHardware: InitHDA() failed

Note: WINCE 6, 3rd Party BSP install Folder and Catalog

寫這篇應該只有對我自己有用了 :P, 畢竟現在才開始做CE6 的人太少了, 以下是我自己嘗試實驗所得

WinCE 6 不在像 CE 5 時代有 Catalog Manager 的設計, 當Visual Studio 2005 with PB 6.0 plug-in 裝好後, 打開 VS2005, PB 會去自動在某些目錄下找 Catalog file , 目前我得知的有下面兩個位置 分別是 “3rdParty", “PUBLIC\ThridParty",

  • $(_WINCEROOT)\3rdParty\$(BSPNAME)\Catalog\*.pbcxml
  • $(_WINCEROOT)\PUBLIC\ThirdParty\Catalog\$(BSPNAME)\$(DRIVER)\Catalog\

P.S. M$ 網站上下載的 USBCAM 擺在第二個位置, 但是他的catalog file 有點小問題, 找到下面這行, 可以看到他固定為 C:\WINCE600

<Project>C:\WINCE600\Public\ThirdParty\Catalog\USBCAM\Driver\USBCAM.pbpxml</Project>

改成下面這樣比較好

<Project>$(_WINCEROOT)\Public\ThirdParty\Catalog\USBCAM\Driver\USBCAM.pbpxml</Project>

Update: 當擺在Public 下的folder 裡面有 Catalog Folder &  Catalog files, 好像就可以正常辨識了 不需要特地放在ThridParty下

Note: Spare Block for SLC NAND Device(x8)

SLC NAND 的 16 bytes Spare 區塊, 在區塊內的安排如下

Small Page (512+16) 是上圖, Bad Block Indicator 被安排在 第 6 個 Byte

Large Page(2048+16*4), 則Bad Block indicator 則被擺到第一個Byte

  • BAD: Bad page indicator, 當值不為0xFF, 表示該頁是壞的
  • LSN: Logical Block Number 提供上層 FTL 記錄邏輯扇區用
  • RESERVED: 也是主要用來提供 FTL 用來做一些演算法利用的儲存區
  • ECC0 ~ ECC2, Main Array 的 ECC Code 儲存區, 因 256 Byte 會用到 22bit 的ECC, 512 BYTE 則用到 24bits 剛好三個BYTE
  • S-ECC0 ~ S-ECC1, Spare 區域用的 ECC 碼, 主要針對 LSNx, 等校正

Ref. Spare Assignment Standard, Samsung App. Note

Note: SLC NAND Flash, Large Block, 2K page size

今年被公司安排, 從 PC BIOS Engineer 轉成了SW Engineer, 重操舊業搞起了WinCE, 因為案子需求, Study 了一下 NAND 的結構,這篇筆記以 2K Large Block 的SLC NAND 為例, 會想寫下筆記是因為網路上看了很多資料, 但是有些過於分散, 寫成code的也缺一些關鍵點, 不適合簡單入門的概念, 題外話, 下圖是用Google Documents 畫的, 現在 Web App 真的是很成熟!

NAND

上圖有三個部分

  • NAND PHYSICAL 是 NAND Flash datasheet 裡面寫的實體頁(page)配置, 2048Byte的Main Array + 64Byte的Spare Array
  • 中間則是 Driver 實作時, 透過邏輯上的概念切割把他分成 四塊, 讓它吻合如 Small Block,而 Sector 512Bytes 的大小
  • 而最下面則是FTL (Flash Translation Layer) 看到的樣子

至於Driver為啥可以做這種概念上的切割, 依據的應該是 Datasheet 中關於 Page programming 的下面這一段話

The device is programmed basically on a page basis, but it does allow multiple partial page programing of a word or consecutive bytes up to 2112, in a single page program cycle. The number of consecutive partial page programming operation within the same page without an intervening erase operation must not exceed 4 times for main array (1time/512byte) and 4 times for spare array(1time/16byte). The addressing should be done in sequential order in a block. A page program cycle consists of a serial data loading period in which up to 2112bytes of data may be loaded into the data register, followed by a non-volatile programming period where the loaded data is programmed into the appropriate cell.

此外 NAND 操作還有一些注意事項, (這裡用1Gb/128M 為例)

  1. Erase 以Block 為單位, 一個 Block 有 64 pages, 每頁 2K+64B
  2. Programming 以page 為單位, page 必須由Block 的page 0 依序寫到 page 63, 不能任意順序寫入

待續, 有空的話將補完 Spare 的業界標準, 1bit ECC 等.. (這兩部分補完的話, SLC NAND應該就打通底層)

Update:

如果不是採用 3rd party 的 FTL Driver, 會有下列限制

WinCE 4.2 只能支援 512B 的 small page 的 SLC NAND,  因為好像未採用 FlashInfo 去Get Sector Size(這個是看來的懶得去查了, 有求知心的可以查查, 確認後方便的話通知我)

WinCE 5.0 & WinCE 6.0 R2 以前, 只能支援 SLC, 因為 FAL (FTL) Driver 會有用到 partial page programming  feature, 換句話說要用 MLC NAND 要在 WinCE 6.0 R2或是之後版本的 MSFLASH 才能正常支援MLC 版本

因為 SLC 因為支援 Partial Page programming 關係 所以實際上也可把上圖的 User View 的結構, 用在Driver , 反正可以寫四次 所以spare 不見得一定要分區寫, 又 spare 要放在最前面也行, 反正只要自己的driver 能確認出data & spare 的定位就行了

Note:ACPI Driver, Reverse Engineering, Part I

ATK200 ACPI vendor driver for Win2K, DispatchCreateClose/DriverEntry/Unload/QueryIF_ACPI

// defined GUID_ACPI_INTERFACE_STANDRD in wdmguid.h
// static const GUID GUID_ACPI_INTERFACE_STANDARD = { 0xb091a08a, 0xba97, 0x11d0, { 0xBD, 0x14, 0x00, 0xaa, 0x00, 0xb7, 0xb3, 0x2a, } };
static ACPI_INTERFACE_STANDARD IF_ACPI;
static NTSTATUS QueryIF_ACPI(PDEVICE_OBJECT PDevObj)
{
NTSTATUS result;
PIRP Irp;
PIO_STACK_LOCATION IrpSp;
KEVENT Event;
IO_STATUS_BLOCK IoSB;

KeInitializeEvent(&Event, SynchronizationEvent, 0);
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, PDevObj->NextDevice, 0, 0, 0, &Event, &IoSB);
IrpSp = IoGetNextIrpStackLocation(Irp);
// Setup ACPI Interface IRP
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
Irp->IoStatus.Information = 0;
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(ACPI_INTERFACE_STANDARD);
IrpSp->Parameters.QueryInterface.Interface = (PINTERFACE)&IF_ACPI;
IrpSp->Parameters.QueryInterface.InterfaceSpecificData = NULL;
// send to next layer driver
result = IofCallDriver(PDevObj->NextDevice, Irp);
if ( STATUS_PENDING == result)
{
KeWaitForSingleObject(&Event, 0, 0, 0, 0);
result = IoSB.Status;
}
return result;
}

NTSTATUS Atk2DispatchOpenClose (
IN PDEVICE_OBJECT PDO,
IN PIRP Irp
)
{

PDEVICE_OBJECT DevExt = PDO->DeviceExtension;
PFILE_OBJECT pFO = IoGetCurrentIrpStackLocation(Irp)->FileObject;

Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;

if (DevExt->AttachedDevice)
{
if( pFO->FileName.Buffer )
{
ObDereferenceObject(DevExt->AttachedDevice);
DevExt->AttachedDevice = NULL;
}
}

IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}

VOID Atk2Unload(IN PDRIVER_OBJECT PDrvObj)
{
PDEVICE_OBJECT PDevObj= PDrvObj->DeviceObject;
PAGED_CODE ();
if (PDevObj)
{
IoDeleteDevice((PDEVICE_OBJECT)PDevObj->DeviceExtension);
}
}

NTSTATUS DriverEntry(
IN PDRIVER_OBJECT  PDO,
IN PUNICODE_STRING RegistryPath
)
{
PDO->MajorFunction[IRP_MJ_CREATE] = Atk2DispatchOpenClose;
PDO->MajorFunction[IRP_MJ_CLOSE]    = Atk2DispatchOpenClose;
PDO->MajorFunction[IRP_MJ_POWER]    = ACPIDispatchPower;
PDO->MajorFunction[IRP_MJ_READ]     = Atk2DispatchReadWrite;
PDO->MajorFunction[IRP_MJ_WRITE]    = Atk2DispatchReadWrite;
PDO->MajorFunction[IRP_MJ_PNP]     = Atk2DispatchPNP;
PDO->MajorFunction[IRP_MJ_DEVICE_CONTROL] = Atk2ForwardRequest;
PDO->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = Atk2ForwardRequest;
PDO->DriverExtension->AddDevice = Atk2AddDriver;
PDO->DriverUnload = (PDRIVER_UNLOAD)Atk2Unload;
DrvInit = 1; // unknow function/Variable
return STATUS_SUCCESS;
}