bloggerads

2014年6月27日 星期五

ATA overview

想要寫一支Driver來控制硬碟發送ATA command,必須了解三份Spec
  1. ATA (AT Attachment): 這是一份軟體的spec,定義了command的種類
  2. AHCI or IDE : 這是controller的spec, 說明如何控制controller發出ATA command給碟機
  3. SATA :定義HW(phy)的行為,如power management及一些encoding/decoding, Link規則...等等 
這邊列舉一些常見的ATA command, 並以我開發的tool, 下圖,來介紹一些常見的command。



  • 25h: Read DMA ext: 傳統的Read
  • 35h: Write DMA ext: 傳統的Write
  • 60h: Read FDMA Queued :這就是 NCQ read
  • 61h: Write FDMA Queued :NCQ write
  • ECh: Identify : 這個command帶有512Byte的table,用來提供host查詢碟機的能力
  • E0h: Standby Immediately: 用於斷碟機電前, host發此命令告訴碟機
  • EFh: Set feature: host可以透過這個command來開啟碟機一些能力,如Partial/Slumber...
  • 06h: Data Set Management :這就是Trim command,用來告訴碟機哪些LBA是無效的資料請碟機刪掉。這個command對SSD尤其重要,若沒有這個command, SSD firmware 裡的GC(Garbage Collection)將不會回收一些無效的LBA而導致硬碟用久後效能低落
  • 92h: Download Microcode: 碟機廠家用此command來update 碟機內部的firmware


2014年6月12日 星期四

UEFI : Boot Service: HandleProtocol () / OpenProtocol (), LocateHandle() / LocateHandleBuffer()


     在寫UEFI code的時候,常常需要找到某個特定的handle, 然後打開這個handle底下特定的Protocol。如找到某個Pci Controller Handle, 然後打開他底下的gEfiPciIoProtocolGuid, 之後才能順利access此controller的Pci Space/Mem Space, 使用的方法不外乎是呼叫Boot Service所提供的HandleProtocol () / OpenProtocol (), LocateHandle() / LocateHandleBuffer()。

這幾個function說明如下:

■ HandleProtocol():

Queries a handle to determine if it supports a specified protocol.
The HandleProtocol() function is still available for use by old EFI applications and drivers. However, all new applications and drivers should use OpenProtocol() in place of HandleProtocol().

注意spec有說明HandleProtocol ()這個function應該要由OpenProtocol()來取代。因為它其實是長這樣的

EFI_STATUS
HandleProtocol (
IN EFI_HANDLE Handle,
IN EFI_GUID *Protocol,
OUT VOID **Interface
)
{
return OpenProtocol (
          Handle,
          Protocol,
          Interface,
          EfiCoreImageHandle,
          NULL,
          EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
          );
}

■ OpenProtocol()

Queries a handle to determine if it supports a specified protocol. If the protocol is supported by the handle, it opens the protocol on behalf of the calling agent. This is an extended version of the EFI boot service HandleProtocol().

■ LocateHandle() / ■ LocateHandleBuffer() <== 這個比較常用!!

先分別看兩個的原型

typedef
EFI_STATUS
(EFIAPI *EFI_LOCATE_HANDLE_BUFFER) (
          IN EFI_LOCATE_SEARCH_TYPE SearchType,
          IN EFI_GUID * Protocol OPTIONAL,
          IN VOID *SearchKey OPTIONAL,
          IN OUT UINTN *NumberHandles,
          OUT EFI_HANDLE **Buffer
);
typedef
EFI_STATUS
(EFIAPI *EFI_LOCATE_HANDLE) (
          IN EFI_LOCATE_SEARCH_TYPE SearchType,
          IN EFI_GUID * Protocol OPTIONAL,
          IN VOID *SearchKey OPTIONAL,
          IN OUT UINTN *BufferSize, // On input, the size in bytes of Buffer. On output, the size in bytes of the array returned in Buffer (if the buffer was large enough) or the size, in bytes, of the buffer needed to obtain the array (if the buffer was not large enough).
          OUT EFI_HANDLE * Buffer // The buffer in which the array is returned
);

Description

The LocateHandle() function returns an array of handles that match the SearchType request. If the input value of BufferSize is too small, the function returns EFI_BUFFER_TOO_SMALL and updates BufferSize to the size of the buffer needed to obtain the array.

兩者差別在LocateHandle()需要自己”分配allocate”跟釋放Buffer。而LocateHandleBuffer() 使用者僅需釋放Buffer,allocate buffer 由此函式做掉

2014年6月9日 星期一

Demonstrate how macro CR works

在UEFI的code裡看到很多這樣的用法, 所以追根究底把它弄懂。用法是: 如果傳進一個函數裡面是某個結構成員的位址,可以用這個巨集回推出主結構的起始位址。 

+    /*    
+    原始定義: 
+    #define _CR(Record, TYPE, Field) 
+          ((TYPE *) ((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *) 0)->Field)))
+      
+    用途: 由結構成員的位址求得整個結構的起始位址
+    
+    Record:該成員的位址
+    TYPE:該成員所屬結構的型態
+    Field:該成員
+    (CHAR8 *) (Record):該TYPE結構成員的實際位址
+    (CHAR8 *) &( ( (TYPE *) 0)->Field):該成員在結構中的相對偏移量
+    
+    兩個做相減之後便可取得結構頭的位址,最後以(TYPE *)作強制轉換。
+    
+    */
+
+    #define _CR(Record, TYPE, Field)   
+      ((TYPE *) ((char *) (Record) - (char *) &( ((TYPE *)0)->Field)) )
+    
+    typedef struct _MyStru {
+            char a;
+            char b;
+            char c;
+    } MY_TYPE;
+    
+    void main()
+    {
+      MY_TYPE *pd;
+        
+      printf("Find pd address by CR: %X \n", _CR(&(pd->c), MY_TYPE, c) );
+      printf("pd address: %X \n", pd );  
+    }

2014年5月29日 星期四

makefile example in windows

path
|
+--scr
|   +--a.txt
|   +--b.txt
|
+--dest


@IF EXIST "src" (
@ECHO src exist
)

REM set current path to variable
@set current=%cd%
@cd %current%/src

REM Don't show command in screen by add @
@

REM Show something
@ECHO Show Something..

REM Copy a a.txt from src folder to dest folder
cp src/a.txt dest

REM Copy all files from src folder to dest
cp -r src/* dest

REM Jum To Tag
@GOTO EXIT
:EXIT

REM create a new folder
mkdir NewFolder

2014年5月26日 星期一

PCI 相關spec 隨手紀錄 (To Be Continued)

# Type 0 PCI header register介紹
讀外插PCI controller的option Rom需先enable option rom access(ERBAR.bit0=1)
=>先將Expansion Rom BAR bit0設為1再access Expansion Rom BAR address(option rom address)

Bios階段enable MMIO access(command.bit1=1), enable IO access(command.bit0=1) 以及打開Bus Master(command.bit2=1)讓device可以主動發出memory cycle

# Configuration Space accessing

1. 如果是寫DOS的程式,透過 Int 1A 可以 access 超過 0xff 這個範圍

2. CF8, CFC方法: (發 IO transaction)
Read 就是Write address to 0xCF8, Read Dword data from 0xCFC 
Write 就是Write address to 0xCF8, Write Dword data to 0xCFC 



+  // 示範Read, Author: Martin Lee
+  DWORD ReadPci( CFG_ADDRESS  addr)
+  {
+      DWORD val;
+      DWORD daddr = addr.all | BIT(31);
+      WORD  oport = 0xcf8;
+      WORD  iport = 0xcfc;
+      
+      __asm{
+      mov eax, daddr
+      mov  dx, oport
+      out  dx, eax
+      mov  dx, iport
+      in  eax, dx
+      mov DWORD ptr val, eax
+      }
+      return val;
+  }

3. 直接MMIO accessing: (發 Memory transaction)
這個方法必須找到MMIO base address(x86通常都會在E000_0000H,這個位址沒有絕對要看chipset的romsip決定)來算出真正Bus/Device/Function/Register的位址,
                                                            
MMIO Address = MMIO_BASE_Addr + { bus number[27:20], device number[19:15], function number[14:12], extended register number[11:8], register number[7:2], offset [1:0] }.

#define PCIEX_BASE_ADDRESS   0xe0000000 
#define PCIE_CFG_ADDR(bus, dev, func, reg) \
((UINTN)(PCIEX_BASE_ADDRESS + ((bus) << 20) + ((dev) << 15) + ((func) << 12) + (reg) ))

# About PCI to PCI bridge:





2014年5月24日 星期六

SATA Power Management

SATA PHY有四種state, 需要注意回到PHYRDY的時間有沒有follow spec
  1. PHYRDY
  2. Partial:  <10us
  3. Slumber: <10ms
  4. DevSleep: <20ms
發動的時機有兩種: HIPM / DIPM,一個是Host主動發起,一個是device主動發。

想知道這個碟機有support什麼樣的power management能力可以看Identify table

 Word 76, bit 9: Support HIPM
 Word 76, bit 14: Support Device auto slumber
 Word 78, bit 3: Support DIPM
 Word 78, bit 8: Support DEVSLP
 Word 79, bit 3: Enable DIPM
 Word 79, bit 8: Enable DEVSLP

以下是控制AHCI controller來做HIPM/DIPM實驗:

HIPM:

  Partial:
  • PxCMD &= (~CMD_ASP) //mute bit 27
  • PxCMD |= CMD_ALPE //set bit 26
  • PxSCTL.IPM =0 // mute bit 11~8

 Slumber:
  • PxCMD |= CMD_ALPE | CMD_ASP //set bit 26 & bit 27
  • PxSCTL.IPM =0 // mute bit 11~8

發個ATA command來trigger, 然後Check PxSSTS.IPM 就知道有沒有成功


DIPM:

 發ATA command(Set Feature, EFh) 開啟碟機這項功能


2014年4月26日 星期六

IDE (Integrated Drive Electronics) controller

IDE是早期用來控制碟機的spec, 目前已完全被AHCI所取代。

Q: 為什麼還是要學IDE protocol呢?
A: 因為Intel目前為止的設計,還是保留著支援IDE的彈性,在一些老系統(如xp)也只有IDE driver。通常都會讓使用者自行透過Bios選項將AHCI controller切換成IDE controller,這時Driver/Bios的控制硬碟方式就要改成IDE,所以還是有學習的價值。

Q: Why not default use IDE in modern PC?
A: IDE原理是控制IO register去下ATA command,除了速度慢以外。最大問題是他的設計是古老的思維而限制住他未來擴充的彈性,因此在2003年Intel release AHCI 用以取代IDE。

+ 本文開始 +

spec請參考以下這三分。古老的spec設計通常都比較簡單易懂
  1. PCI IDE Controller Specification Revision  1.0    3/4/94
  2. Programming Interface for Bus Master IDE Controller Revision  1.0     5/16/94
  3. Information Technology - AT Attachment - 8 ATA/ATAPI Command Set 
一個IDE controller規定了四個channel, 基本上有兩種mode, Compatibility and Native。

Compatibility mode:
  • 固定的IO register(Primary cmd/ctrl = 0x1f0~0x1f7/0x3f6, Secondary cmd/ctrl = 0x170~0x177/0x376)位址和固定的IRQ(14, 15)。
Native mode:
  • 由系統來assign: Primary cmd/ctrl = bar#0/bar#1, Secondary cmd/ctrl = bar#2/bar#3, 
需要注意的是control register 的位址是 ctrl port + 2

Q: 如何知道現在這個IDE是Compatibility or Native?
A: 在PCI header, Base-Class(09h), Sub-Class(0Ah), Interface:(0Bh) 定義了class code, 如果programmable indicator 不是fixed,operating mode可由程式來填入改變

Programmable Indicator: Indicate if controller support both mode (1), or fixed mode(0)
Operating Mode: Determine mode (0: compatibility, 1: native) 
Q: 如何找到硬碟插在哪個channel上並且發送ATA command?
A: 可以透過設定device register的bit 4, 然後看status是否回0x50來判斷channel上是否有device, 


接著請參考ATA spec定義的欄位,填寫要發的 command


Q: Bus Master code怎麼寫?
A: Bar#4 給定了Bus Master IO base address, 剩下的部分請參考圖,和原文spec step by step, 照做就可以把code寫出來了。


注意Command Register的Bit 3(read/write control), 下read command 時 bit3要設為 1, write command 時 bit3設為 0
programming bus master IDE, 請參考以下原文
3.1. Standard Programming Sequence
To initiate a bus master transfer between memory and an IDE DMA slave device, the following steps are required:

1) Software prepares a PRD Table in system memory. Each PRD is 8 bytes long and consists of an address pointer to the starting address and the transfer count of the memory buffer to be transferred. In any given PRD Table, two consecutive PRDs are offset by 8-bytes and are aligned on a 4-byte boundary.


2) Software provides the starting address of the PRD Table by loading the PRD Table Pointer Register . The direction of the data transfer is specified by setting the Read/Write Control bit. Clear the Interrupt bit and Error bit in the Status register.


3) Software issues the appropriate DMA transfer command to the disk device.


4) Engage the bus master function by writing a '1' to the Start bit in the Bus Master IDE Command Register for the appropriate channel.


5) The controller transfers data to/from memory responding to DMA requests from the IDE device.


6) At the end of the transfer the IDE device signals an interrupt.


7) In response to the interrupt, software resets the Start/Stop bit in the command register. It then reads the controller status and then the drive status to determine if the transfer completed


successfully.