在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 );
+ }
bloggerads
2014年6月9日 星期一
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
想知道這個碟機有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實驗:
Slumber:
發個ATA command來trigger, 然後Check PxSSTS.IPM 就知道有沒有成功
- PHYRDY
- Partial: <10us
- Slumber: <10ms
- DevSleep: <20ms
想知道這個碟機有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設計通常都比較簡單易懂
Compatibility mode:
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設計通常都比較簡單易懂
- PCI IDE Controller Specification Revision 1.0 3/4/94
- Programming Interface for Bus Master IDE Controller Revision 1.0 5/16/94
- Information Technology - AT Attachment - 8 ATA/ATAPI Command Set
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?
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.
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.
2014年4月14日 星期一
PCIe Capability (0x10)
列出常參考的部分:
# Offset 0x0C (Link Capabilities)
=> 電源控制 bit[1:0]: ASPM, 00b(Disabled) / 01b(L0s) / 10b(L1) /11b(L0s+L1)
# Offset 0x12 (Link Status)
=> 速度 bit[3:0]: 1=>Gen1(2.5GT), 2=>Gen2(5GT), 3=>Gen3(8GT), Gen1/Gen2採8b/10b編碼,實際速度約打8折
=> 寬度 bit[9:4]: 1/2/4/8/16=>x1/x2/x4/x8/x16 lane
2014年4月5日 星期六
AHCI (Advanced Host Controller Interface)
AHCI是PC架構下常見的儲存裝置控制器,透過AHCI我們可以將ATA command以SATA的形式發送給device。
一個AHCI controller最多可以implement 32個port,AHCI spec厲害的地方是定義了NCQ command。也就是host可以先發送最多32個command給device, 由device來決定要先做哪個command。近年來拜NAND flash快速發展所賜, 配上NCQ command,硬碟終於可以跑到接近SATA 3所定義的物理極速600MB/s。請參考下面這張TOSHIBA 120G的SSD跑分
焦點拉回spec,AHCI在PC架構上是一個PCI 或PCIe controller, 他的class code是Rx[B:9]=0x010601。
MMIO base address定義在PCI CFG header就Bar#4,在開機時,Bios會依照AHCI spec要求assign 一個align to 8KB的MMIO的base address。access MMIO address 可以看到General Host Control Registers(0~0xFF) + port0~31 (每個port range為0x80)這兩大部分
從GHC registers我們可以知道這個controller的能力如這個AHCI controller implement哪些port可以讓software使用,發生中斷的原因以及透過這些register做Global host reset。
至於對某個port上的硬碟發command最主要是透過port control registers來辦到。透過port的register我們也可以發port reset這種局部reset來命令此port的SATA phy重新link。
Q: 如何對port發command?
A: 首先先把記憶體宣告好,把要發的command以 command FIS型態填在宣告的記憶體上,如果是帶資料的command則還需要把PRD Table描述好,並將其指到一個準備傳送或接收資料的記憶體位址(注意必須是Word aligned)。最後去set PxCI,command就可以發到碟機了。
最後附上精心製作的AHCI register表,祝大家都可以寫出屬於自己的AHCI driver
![]() |
| Overall Registers layout |
![]() |
| PRDT layout |
訂閱:
文章 (Atom)
















