2018年10月29日 星期一

Memory/IO/PCI

電腦會把所要執行的程式和資料載入到記憶體裡執行, 組合語言有定義明確的資料區段和程式區段, C語言其實也有只是它是隱含的沒有顯露出來, 舉個最常見的例子, 全域變數(global)被載入記憶體後就被放在資料段, 以EFI架構來說一個全域變數理想狀況是可以被四大phase裡同一個phase的所有模組存取的, 記憶體的圖示如下, 存取方式之前提過了, 只是因為是32位元電腦所以位址變成32-BIT(4-BYTE)
IO就比較神奇了, 看名字就知道它是Input和Output, 電腦允許0x0000到0xFFFF 16-BIT寬的IO PORT(IO埠), 這些IO PORT和記憶體是不一樣的, 比如我們把資料丟到IO PORT 0x80, 這裡的80指的是IO PORT 0x80不是記憶體0x80(IO PORT 80是七段顯示器, 用來除錯), 提到IO PORT就一定要提及Index/Data存取方式, CMOS(電腦電池), superior(便宜的電腦沒有EC常用SuperIo代替), 甚至接下來要介紹的PCI-e都是用Index/Data存取方式, 那麼甚麼是Index/Data存取方式呢? 比如CMOS的Index是0x70, Data是0x71, 當我在IO PORT 0x70寫入0x12, IO PORT 0x71寫入0x34, 實際上CMOS的0x12位址(這裡的位址不是記憶體位址, 只是類似記憶體的概念)裡面的資料會變成0x34, 就像下圖, 至於SuperIo的Index/Data有兩種可能(印象中硬體接線好像可以選擇, 不太確定)就是0x2E/0x2F和0x4E/0x4F
PCIe/PCI就更神奇了, PCI和PCIe是不同的, PCI用Index/Data存取資料, 它的Index是0xCF8, Data是0xCFC, PCIe雖然也可以用CF8/CFC存取資料, 但這種方法只能存取前面的256-BYTE, 所以PCIe還有另一種存取方式叫記憶體映射(Memory Mapping IO, MMIO), 就是把記憶體當成IO PORT來使用, 這兩種方法接下來都會有實例介紹首先是CF8/CFC的方法, 先以PCI來舉例, PCI介面有所謂Bus, Device, Function, 一般都簡寫成Bus, Dev, Fun, PCI(PCI-e也一樣)共有2的8次方個Bus, 2的5次方個Dev, 2的3次方個Fun, 再配上2的8次方共256個暫存器, 所以用CF8/CFC存取時格式應該是像下面:

0x80000000 + (Bus << 16) + (Dev << 11) + (Fun << 8) + 暫存器(2的8次方) ==> INDEX

0x80000000是用CF8/CFC法存取時一定要加上的Base Address, Fun左移8-BIT是因為有2的8次方個暫存器, 又因為Fun有2的3次方個所以Dev左移8+3=11, 而Dev有2的5次方所以Bus左移11+5=16個位元(BIT), 因為我不知道怎麼用C語言存取IO, 所以我以組合語言作範例如下 : 

MOV  EAX,  INDEX
MOV  DX,    CF8H
OUT   DX,    EAX
MOV  EAX,  DATA
MOV  DX,    CFCH
OUT   DX,    EAX

在這裡要注意一個重點
, PCI和PCI-e都是以32-BIT為單位存取的, 所以INDEX一定要是4的倍數, 如果不是4的倍數按照我的經驗會時好時壞不太靈光, 舉例來說


INDEX = 0x80000804
DATA = 0x12345678

則0x80000804 = 0x78, 0x80000805 = 0x56, 以此類推
而實際達成是用IO CFC寫到0x80000804, IO CFD寫到0x80000805, 一直寫到0x80000807

PCIe(其實現在業界都用PCIe, PCI已經算幾乎被淘汰了)也可以用CF8/CFC的方法存取資料, 但是PCIe有2的12次方(4K)個暫存器, 用CF8/CFC方法只能存取前256-BYTE的暫存器, 所以PCIe另有一套MMIO存取法, 示例如下:

PCIE_BASE_ADDRESS + (Bus << 20) + (Dev << 16) + (Fun << 12) + 2的12次方個暫存器

在NT32裡PCI_BASE_ADDRESS是0xE0000000, 記得2的12次方暫存器最好也用4的倍數存取

沒有留言:

張貼留言